LoginSignup
0

More than 1 year has passed since last update.

Pleasanterの説明項目の文字に色をつけたい

Last updated at Posted at 2022-09-17

はじめに

プリザンターは一覧表示で情報を管理する用途では、思い立ってからアプリが動き出すまでが圧倒的短時間で作れるノーコードツールです。多くのツールは白紙のキャンバスを前に途方に暮れるのですが、プリザンターはこの段階が無いのが大きな利点でお気に入りです。
……お気に入りなのですが、ものすごく不満と言いますか、ダメだろコレと思う部分があるんです。それは、文章の文字に「色」を付けることが出来ないところです。
リッチテキストの編集機能とまでは言いませんが、やはり説明文書の注意事項は赤文字にしたいじゃないですか!

方針

本体のソースコードをいじる根性は無いため、ブラウザに表示した後で文字に色を付けたいと思います。プリザンターの独自JavaScriptコードをロードし放題の最高拡張機能を使って、色を付けたい文字を<span><font>タグで囲えば行けそうです。(ブラウザのDevToosを使って書き換えたら色がつきました)

とは言っても色指定の修飾タグを独自に考えるのは面倒なのでMarkdownの場合のみ変更したいと思います。プリザンターが<span>&lt;span&gt;とエスケープしてくれている部分を元に戻すわけですね。

検証環境

Pleasanter:1.3.20.0
web browser:chrome

ソースコード

サンプルコード
//////////////////////////////////////////////////
// MarkDownモードの時に<sapn><font>タグで文字色を指定可能とする
// 実現方法は編集画面のHTMLを書き換えているだけ。根本解決では無い
//////////////////////////////////////////////////

//////////////////////////////////////////////////
// 監視に使うSelectorの作成
// columnName:英語の項目名(Body,DescriptionA〜Z)
//////////////////////////////////////////////////
function makeSelector(columnName){
  let selectorSet = {};   // 返却用データの入れ物
  let selector_target     // 変更検出用セレクタ(MutationObserverの監視node)

  // 各テーブルタイプの判定
  switch ($p.tableName()) {
    case 'Issues':
        // 期限付きテーブル
        selector_target = '#Issues_' + columnName + '\\.viewer';
        break;
    case 'Results':
        // 記録テーブル
        selector_target = '#Results_' + columnName + '\\.viewer';
        break;
    case 'Sites':
    case 'Wikis':
    default:
      return selectorSet;
  }

  // MarkDown領域の検出用セレクタ作成
  let selector_md = selector_target + '>div.md';
  let selector_html_span_Tag = '>p:contains("<span")';  // 文字色対応用("<span style="にした方が安全だと思う)
  let selector_html_font_Tag = '>p:contains("<font")';  // 文字色対応用("<font style="にした方が安全だと思う)
  let selector_html_Code_Tag = '>p>code';               // [`]対応用(インラインコード:応用例)
  let selector_html_Code2_Tag = '>pre>code';            // [```]対応用(コードブロック:応用例)
  let selector_html_Tag = selector_md + selector_html_span_Tag;
  selector_html_Tag += ','+selector_md + selector_html_font_Tag;
  selector_html_Tag += ','+selector_md + selector_html_Code_Tag;
  selector_html_Tag += ','+selector_md + selector_html_Code2_Tag;

  selectorSet['columnName'] = columnName;
  selectorSet['selector_target'] = selector_target;
  selectorSet['selector_md'] = selector_md;
  selectorSet['selector_html_Tag'] = selector_html_Tag;

  return(selectorSet);
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <span>
//////////////////////////////////////////////////
function restoreHtmlTag_span(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  return contentsText.replace(/&lt;span([0-9a-zA-Z=\-:\.,; "'=\t]*?)&gt;(.*?)&lt;\/span&gt;/g, '<span$1>$2</span>');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <font>
//////////////////////////////////////////////////
function restoreHtmlTag_font(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  return  contentsText.replace(/&lt;font([0-9a-zA-Z=\-:\.,; "'=\t]*?)&gt;(.*?)&lt;\/font&gt;/g, '<font$1>$2</font>');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <code>
//////////////////////////////////////////////////
function restoreHtmlTag_code(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  // [`]インラインコードと[```]コードブロックの対応用<code>タグの中
  return contentsText.replace(/&amp;lt;([0-9a-zA-Z=\-:\.,; "'=\/\t]*?)&amp;gt;/g, '&lt;$1&gt;');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <br>
//////////////////////////////////////////////////
function restoreHtmlTag_br(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全だと思う。
//  return contentsText.replace(/(?<!<code>)&lt;br&gt;(?!<code>)/g, '<br>');
  return contentsText.replace(/(?<!<code>)&lt;br&gt;(?!<code>)/g, '<br>');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元
//////////////////////////////////////////////////
function restoreHtmlTag(contentsText){
  return restoreHtmlTag_code(restoreHtmlTag_font(restoreHtmlTag_span(contentsText)));
}

//////////////////////////////////////////////////
// コメント欄のMarkdown変更
//////////////////////////////////////////////////
function commentRestoreHtmlTag() {
  let selector_comment_Tag = '#CommentList p.body.markup.applied>div.md';     // MarkDown領域検出用セレクタ作成
  let commentList = document.querySelectorAll(selector_comment_Tag);
  commentList.forEach((commentListElm,index_commentList) => {
    $(commentListElm).html(restoreHtmlTag(restoreHtmlTag_br($(commentListElm).html())));
  });
};

//////////////////////////////////////////////////
// 内容項目と説明項目のMarkdown変更
//////////////////////////////////////////////////
function descriptionRestoreHtmlTag(selectorSet) {
  let divMds = document.querySelectorAll(selectorSet.selector_md);
  divMds.forEach((divMd,index_Md) => {
    $(divMd).html(restoreHtmlTag_br($(divMd).html()));  // div.mdノード全体で<br>改行対応
    let markdowntNodes = $(selectorSet.selector_html_Tag);
    markdowntNodes.each((index,markdownElm) => {
      $(markdownElm).html(restoreHtmlTag($(markdownElm).html()));
    });
  });
}

Markdown_ThroughHtmlTag:{
  let selectorSets = {};    // 監視対象のselector情報

  //////////////////////////////////////////////////
  // 内容項目と説明項目のDOM変更を監視してDOMの上書きを行う
  //////////////////////////////////////////////////
  function setDescriptionRestoreHtmlTag(){
    let hashIdArray = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];

     // 説明項目と内容項目のセレクタを作成
    let columnName = 'Body';
    let selectorSet = makeSelector(columnName);
    if(Object.keys(selectorSet).length != 0){
      selectorSets[columnName] = selectorSet;
    }
    hashIdArray.forEach((hashId) => {
      columnName = 'Description'+ hashId;
      selectorSet = makeSelector(columnName);
      if(Object.keys(selectorSet).length != 0){
        selectorSets[columnName] = selectorSet;
      }
    });

    // 内容項目と説明項目の変更を監視開始
    Object.keys(selectorSets).forEach(key => {
      selectorSets[key]['observer'] = setMutationObserver(selectorSets[key]);
    });

    //////////////////////////////////////////////////
    // DOM変更を監視の登録
    function setMutationObserver(selectorSet){
      // nodeの存在確認
      let node = document.querySelectorAll(selectorSet.selector_target);
      if(node.length == 0){
        return;
      }
      let observeNode = node[0]; // 監視対象のNodeをセット

      // オブザーバの設定値
      const observeConfig = {
        // childList、attributes、characterDataいずれかにtrue必須
        childList: true,       // 対象ノードの子ノード(テキストノードも含む)に対する追加・削除を監視する場合はtrue。
        attributes: true,      // 対象ノードの属性に対する変更を監視する場合はtrue。
        characterData: true,   // 対象ノードのデータに対する変更を監視する場合はtrue。
        subtree: true          // 対象ノードの子孫ノードまで監視する場合はtrue。
      }

      // オブザーバインスタンスを作成
      const observer = new MutationObserver((mutations) => {
        // 更新検出時のコールバック関数
        console.log( "内容項目と説明項目 変更検出個数"+ mutations.length);
        observer.disconnect();                          // 監視の停止
        descriptionRestoreHtmlTag(selectorSet);         // 内容項目・説明項目のMarkdownの書換処理
        observer.observe(observeNode, observeConfig);   // 監視の再開
      });
      // オブザーバの開始
      observer.observe(observeNode,observeConfig);

      return(observer);
    }
  }

  //////////////////////////////////////////////////
  // コメント欄のDOM変更を監視してDOMの上書きを行う
  //////////////////////////////////////////////////
  function setCommentRestoreHtmlTag(){
    // nodeの存在確認
    let node;
    node = document.querySelectorAll('#CommentList');
    if(node.length == 0){
      return;
    }
    let observeNode = node[0]; // 監視対象のNodeをセット

    // オブザーバの設定値
    const observeConfig = {
      // childList、attributes、characterDataいずれかにtrue必須
      childList: true,       // 対象ノードの子ノード(テキストノードも含む)に対する追加・削除を監視する場合はtrue。
      attributes: true,      // 対象ノードの属性に対する変更を監視する場合はtrue。
      characterData: true,   // 対象ノードのデータに対する変更を監視する場合はtrue。
      subtree: true          // 対象ノードの子孫ノードまで監視する場合はtrue。
    }

    // オブザーバインスタンスを作成
    const observer = new MutationObserver((mutations) => {
      // 更新検出時のコールバック関数
      console.log( "コメント欄 変更検出個数"+ mutations.length);
      observer.disconnect();                          // 監視の停止
      commentRestoreHtmlTag();                         // コメント欄のMarkdownの書換処理
      observer.observe(observeNode, observeConfig);   // 監視の再開
    });
    // オブザーバの開始
    observer.observe(observeNode,observeConfig);

    return(observer);
  }

  // 「更新」ボタンを押した場合の処理
  $p.events.after_set_Update = function () {
    // この時点でPleasanterがDOMの動的更新が完成している。
    commentRestoreHtmlTag();                        // コメント欄のMarkdownを更新
    Object.keys(selectorSets).forEach(key => {
      descriptionRestoreHtmlTag(selectorSets[key])  // 説明項目と内容項目のMarkdownを更新
    });

    // 「更新」ボタンでDOMが再構築されため再度監視設定
    setDescriptionRestoreHtmlTag();
  }

  // ブラウザで編集画面を読み込んだ時の処理(開いた時と再読み込みした時)
  document.addEventListener( "DOMContentLoaded", () => {
    // DOM構築完了後に実行
    // この時点でPleasanterのDOMの動的更新は完成していない。
    setCommentRestoreHtmlTag();       // コメント欄のMarkdownを監視設定
    setDescriptionRestoreHtmlTag();   // 説明項目と内容項目のMarkdownを監視設定
  });
}

実行方法

  1. 「サンプルコード」を開いてコードを表示する。
  2. ソースコードをコピーする。
  3. プリザンターの「テーブルの管理」を開く
  4. 「スクリプト」タブで新規作成
  5. ソースコードをペーストする。
  6. 出力先に「編集」を選択する。

解説

スクリプトを作るには、変換する<span>タグの場所がわからないと話になりませんからDevToolを使って探します。
DevToolでDOMの構造を見ると、内容項目(Body)のMarkdownの場合は#Issues_Body.viewerの下にdiv.mdができて、子エレメントの<p>タグの中に文字列が入ってるようです。
これで検索する場所がわかりました。

はじめに必要な部品を作っていきます。
検索用のセレクタはBodyDescriptionA〜Z分を作る必要があるためセレクタを作成する関数をつくります。

//////////////////////////////////////////////////
// 監視に使うSelectorの作成
// columnName:英語の項目名(Body,DescriptionA〜Z)
//////////////////////////////////////////////////
function makeSelector(columnName){
  let selectorSet = {};   // 返却用データの入れ物
  let selector_target     // 変更検出用セレクタ(MutationObserverの監視node)

  // 各テーブルタイプの判定
  switch ($p.tableName()) {
    case 'Issues':
        // 期限付きテーブル
        selector_target = '#Issues_' + columnName + '\\.viewer';
        break;
    case 'Results':
        // 記録テーブル
        selector_target = '#Results_' + columnName + '\\.viewer';
        break;
    case 'Sites':
    case 'Wikis':
    default:
      return selectorSet;
  }

  // MarkDown領域の検出用セレクタ作成
  let selector_md = selector_target + '>div.md';
  let selector_html_span_Tag = '>p:contains("<span")';  // 文字色対応用("<span style="にした方が安全だと思う)
  let selector_html_font_Tag = '>p:contains("<font")';  // 文字色対応用("<font style="にした方が安全だと思う)
  let selector_html_Code_Tag = '>p>code';               // [`]対応用(インラインコード:応用例)
  let selector_html_Code2_Tag = '>pre>code';            // [```]対応用(コードブロック:応用例)
  let selector_html_Tag = selector_md + selector_html_span_Tag;
  selector_html_Tag += ','+selector_md + selector_html_font_Tag;
  selector_html_Tag += ','+selector_md + selector_html_Code_Tag;
  selector_html_Tag += ','+selector_md + selector_html_Code2_Tag;

  selectorSet['columnName'] = columnName;
  selectorSet['selector_target'] = selector_target;
  selectorSet['selector_md'] = selector_md;
  selectorSet['selector_html_Tag'] = selector_html_Tag;

  return(selectorSet);
}

次に文字列を変換する関数を作ります。
プリザンターは<span style="color:red">タグは&lt;span style="color:red"&gt;に変換しますから、この逆の処理をすればブラウザが認識するHtmlタグの完成です。
しかし、単純に置き換えたらコードブロックやインラインコードの&lt;span style="color:red"&gt;もHtmlタグに戻るため除外する対応が必要です。と思いましたが、プリザンターはコードブロックの中の<span style="color:red">&amp;lt;span style="color:red"&amp;gt;に置き換えていました。別の文字列のため検索に引っかかりません。やったね!
(でも、このままではコードブロックの見た目が悪いので対応しますよ。)

以下の正規表現は「良く解らないけど動作しているからヨシ!」なので適時修正してください。
(正規表現は魔法の呪文として記憶してるので…)

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <span>
//////////////////////////////////////////////////
function restoreHtmlTag_span(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  return contentsText.replace(/&lt;span([0-9a-zA-Z=\-:\.,; "'=\t]*?)&gt;(.*?)&lt;\/span&gt;/g, '<span$1>$2</span>');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <font>
//////////////////////////////////////////////////
function restoreHtmlTag_font(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  return  contentsText.replace(/&lt;font([0-9a-zA-Z=\-:\.,; "'=\t]*?)&gt;(.*?)&lt;\/font&gt;/g, '<font$1>$2</font>');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <code>
//////////////////////////////////////////////////
function restoreHtmlTag_code(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全
  // [`]インラインコードと[```]コードブロックの対応用<code>タグの中
  return contentsText.replace(/&amp;lt;([0-9a-zA-Z=\-:\.,; "'=\/\t]*?)&amp;gt;/g, '&lt;$1&gt;');
}

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元 <br>
//////////////////////////////////////////////////
function restoreHtmlTag_br(contentsText){
  // 書式が固定できるなら検索ルールは厳しめにした方が安全だと思う。
  return contentsText.replace(/(?<!<code>)&lt;br&gt;(?!<code>)/g, '<br>');
}

Safariは後読み正規表現には対応してませんから、contentsText.replace(/(?<!<code>)&lt;br&gt;(?!<code>)/g, '<br>');で例外がでます。以下のような感じで逃げるなどしてください。(正規表現は良く解らないから、正規表現から逃げてますね)

let text = contentsText.replace(/<code>&lt;br&gt;<code>/g, '<code>&amp;lt;br&amp;gt;<code>');
text = text.replace(/&lt;br&gt;/g, '<br>');
text = text.replace(/<code>&amp;lt;br&amp;gt;<code>/g, '<code>&lt;br&gt;<code>');

実行順番に依存関係がありそうで、複数箇所で使う変換をまとめて関数にします。

//////////////////////////////////////////////////
// MarkDownのHTML Tagエスケープの復元
//////////////////////////////////////////////////
function restoreHtmlTag(contentsText){
  return restoreHtmlTag_code(restoreHtmlTag_font(restoreHtmlTag_span(contentsText)));
}

DOMからセレクタでノードを取得して文字列を置換する関数です。ループを回しているだけですね。コメント欄用もつくりました。
JQueryを使ったり使わなかったりしている部分は、selector_html_Tagの中にJQueryでしか使えないセレクタが入っているからです。

//////////////////////////////////////////////////
// コメント欄のMarkdown変更
//////////////////////////////////////////////////
function commentRestoreHtmlTag() {
  let selector_comment_Tag = '#CommentList p.body.markup.applied>div.md';     // MarkDown領域検出用セレクタ作成
  let commentList = document.querySelectorAll(selector_comment_Tag);
  commentList.forEach((commentListElm,index_commentList) => {
    $(commentListElm).html(restoreHtmlTag(restoreHtmlTag_br($(commentListElm).html())));
  });
};

//////////////////////////////////////////////////
// 内容項目と説明項目のMarkdown変更
//////////////////////////////////////////////////
function descriptionRestoreHtmlTag(selectorSet) {
  let divMds = document.querySelectorAll(selectorSet.selector_md);
  divMds.forEach((divMd,index_Md) => {
    $(divMd).html(restoreHtmlTag_br($(divMd).html()));  // div.mdノード全体で<br>改行対応
    let markdowntNodes = $(selectorSet.selector_html_Tag);
    markdowntNodes.each((index,markdownElm) => {
      $(markdownElm).html(restoreHtmlTag($(markdownElm).html()));
    });
  });
}

ここからが処理フローになります。フロー部はMarkdown_ThroughHtmlTag:{とラベル付きブロックで全体を囲っていますが、グローバルスコープのlet selectorSetsを消せなかったため、気持ち悪いので{}で囲っています。(囲わなくても良いです。)

初めにBodyとDescriptionA〜Zのセレクタを作ります。
そして、内容項目と説明項目の変更の監視を開始します。
当初は$p.events.on_editor_loadのイベントで変換処理をすれば良いと考えていましたが、よくよく考えたら編集画面ロード後に内容項目や説明項目は内容を変更しますよね。
毎回、「変更」->「更新」->「リロード」と言う仕様は無いですからMutationObserverでDOMの変更を監視しています。

Markdown_ThroughHtmlTag:{
  let selectorSets = {};    // 監視対象のselector情報

  //////////////////////////////////////////////////
  // 内容項目と説明項目のDOM変更を監視してDOMの上書きを行う
  //////////////////////////////////////////////////
  function setDescriptionRestoreHtmlTag(){
    let hashIdArray = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];

     // 説明項目と内容項目のセレクタを作成
    let columnName = 'Body';
    let selectorSet = makeSelector(columnName);
    if(Object.keys(selectorSet).length != 0){
      selectorSets[columnName] = selectorSet;
    }
    hashIdArray.forEach((hashId) => {
      columnName = 'Description'+ hashId;
      selectorSet = makeSelector(columnName);
      if(Object.keys(selectorSet).length != 0){
        selectorSets[columnName] = selectorSet;
      }
    });

    // 内容項目と説明項目の変更を監視開始
    Object.keys(selectorSets).forEach(key => {
      selectorSets[key]['observer'] = setMutationObserver(selectorSets[key]);
    });

MutationObserverの処理は教科書通りです。
(ほんとMutationObserverは便利ですよね。)

    //////////////////////////////////////////////////
    // DOM変更を監視の登録
    function setMutationObserver(selectorSet){
      // nodeの存在確認
      let node = document.querySelectorAll(selectorSet.selector_target);
      if(node.length == 0){
        return;
      }
      let observeNode = node[0]; // 監視対象のNodeをセット

      // オブザーバの設定値
      const observeConfig = {
        // childList、attributes、characterDataいずれかにtrue必須
        childList: true,       // 対象ノードの子ノード(テキストノードも含む)に対する追加・削除を監視する場合はtrue。
        attributes: true,      // 対象ノードの属性に対する変更を監視する場合はtrue。
        characterData: true,   // 対象ノードのデータに対する変更を監視する場合はtrue。
        subtree: true          // 対象ノードの子孫ノードまで監視する場合はtrue。
      }

      // オブザーバインスタンスを作成
      const observer = new MutationObserver((mutations) => {
        // 更新検出時のコールバック関数
        console.log( "内容項目と説明項目 変更検出個数"+ mutations.length);
        observer.disconnect();                          // 監視の停止
        descriptionRestoreHtmlTag(selectorSet);         // 内容項目・説明項目のMarkdownの書換処理
        observer.observe(observeNode, observeConfig);   // 監視の再開
      });
      // オブザーバの開始
      observer.observe(observeNode,observeConfig);

      return(observer);
    }
  }

こちらがコメント欄用の監視

  //////////////////////////////////////////////////
  // コメント欄のDOM変更を監視してDOMの上書きを行う
  //////////////////////////////////////////////////
  function setCommentRestoreHtmlTag(){
    // nodeの存在確認
    let node;
    node = document.querySelectorAll('#CommentList');
    if(node.length == 0){
      return;
    }
    let observeNode = node[0]; // 監視対象のNodeをセット

    // オブザーバの設定値
    const observeConfig = {
      // childList、attributes、characterDataいずれかにtrue必須
      childList: true,       // 対象ノードの子ノード(テキストノードも含む)に対する追加・削除を監視する場合はtrue。
      attributes: true,      // 対象ノードの属性に対する変更を監視する場合はtrue。
      characterData: true,   // 対象ノードのデータに対する変更を監視する場合はtrue。
      subtree: true          // 対象ノードの子孫ノードまで監視する場合はtrue。
    }

    // オブザーバインスタンスを作成
    const observer = new MutationObserver((mutations) => {
      // 更新検出時のコールバック関数
      console.log( "コメント欄 変更検出個数"+ mutations.length);
      observer.disconnect();                          // 監視の停止
      commentRestoreHtmlTag();                         // コメント欄のMarkdownの書換処理
      observer.observe(observeNode, observeConfig);   // 監視の再開
    });
    // オブザーバの開始
    observer.observe(observeNode,observeConfig);

    return(observer);
  }

そして、ここからが編集画面が開いたら実行される部分です。
初めに「更新」ボタンが押された時の処理を、$p.events.after_set_Updateに設定します。
$p.events.after_set_Updateが呼ばれた時点で、プリザンターがDOMの動的変更を完成させていました。以後はユーザが変更しない限りDOMの変更が発生しません。そのため、手動でcommentRestoreHtmlTagdescriptionRestoreHtmlTagを走らせてHtmlタグに変換した後でMutationObserverによる監視を開始しています。

  // 「更新」ボタンを押した場合の処理
  $p.events.after_set_Update = function () {
    // この時点でPleasanterがDOMの動的更新が完成している。
    commentRestoreHtmlTag();                        // コメント欄のMarkdownを更新
    Object.keys(selectorSets).forEach(key => {
      descriptionRestoreHtmlTag(selectorSets[key])  // 説明項目と内容項目のMarkdownを更新
    });

    // 「更新」ボタンでDOMが再構築されため再度監視設定
    setDescriptionRestoreHtmlTag();
  }

編集画面のロード時の処理は、DOMContentLoadedにイベント登録してブラウザがDOMを構築してからスクリプトが開始されるようにしています。この辺の順番関係に疎いので安全策です。
ただし、「更新」ボタンが押された時の処理と違って、この時点では、まだプリザンターの動的なオブジェクトは生成前だったため、ここで監視を登録した後でDOMの変更イベントが発生します。そのためhtmlタグへの変換は変更イベントに任せて手動は行いません。(と言うより、まだdiv.mdが作られて無いため手動実行できません。)

  // ブラウザで編集画面を読み込んだ時の処理(開いた時と再読み込みした時)
  document.addEventListener( "DOMContentLoaded", () => {
    // DOM構築完了後に実行
    // この時点でPleasanterのDOMの動的更新は完成していない。
    setCommentRestoreHtmlTag();       // コメント欄のMarkdownを監視設定
    setDescriptionRestoreHtmlTag();   // 説明項目と内容項目のMarkdownを監視設定
  });
}

あと、ブラウザが編集画面を読み込んだ時のみsetCommentRestoreHtmlTagを走らせているのは、コメントの追加は必ず「更新」ボタンを押すからです。コメント欄の変換処理は$p.events.after_set_Updateで行えば対応できます。

結果

##サンプルMarkDown

[md]
# 見出し1
## 見出し2

この部分が<span style="color:red">赤色</span>で表示されます。  
この部分が<font style="color:red">赤色</font>で表示されます。  
この部分が<span style="color:gray">gray色</span>で表示されます  
この部分が<span style="color:silver">silver色</span>で表示されます  
この部分が<span style="color:blue">blue色</span>で表示されます  
この部分が<span style="color:navy">navy色</span>で表示されます  
この部分が<span style="color:teal">teal色</span>で表示されます  
この部分が<span style="color:green">green色</span>で表示されます  
この部分が<span style="color:lime">lime色</span>で表示されます  
この部分が<span style="color:aqua">aqua色</span>で表示されます  
この部分が<span style="color:yellow">yellow色</span>で表示されます  
この部分が<span style="color:red">red色</span>で表示されます  
この部分が<span style="color:fuchsia">fuchsia色</span>で表示されます  
この部分が<span style="color:olive">olive色</span>で表示されます  
この部分が<span style="color:purple">purple色</span>で表示されます  
この部分が<span style="color:maroon">maroon色</span>で表示されます  
この部分が<span style="color:red">赤色</span>で表示されます。この部分が<span style="color:red">赤色</span>で表示されます。この部分が<span style="color:red">赤色</span>で表示されます。


これはインラインコード=> `<span style="color:red">` htmlタグになると困る

```
ここはコードブロック<span style="color:red">赤になると困る</span>htmlタグになると困る
ここはコードブロック<span style="color:red">赤になると困る</span>htmlタグになると困る
ここはコードブロック<span style="color:red">赤になると困る</span>htmlタグになると困る
```   
空行改行する場合は全角スペース2個の後に半角スペース2個  
    
    
    
では、やってられないので`<br>`タグにも対応した。
<br><br><br><br><br><br>
この部分が<span style="color:red">赤色赤色赤色赤色</span>で表示されます  

変換後の画面キャプチャ

Pleasanter画面1.png

あとがき

プリザンターに、リッチテキストの編集画面が欲しいです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0