公式ドキュメントよりも詳しいTinyMCEの使い方(応用編)


投稿日:2018年2月23日
  • 5
  • 0



TinyMCEをとことんまでカスタマイズしたい方向け

前のページでTinyMCEの基本的なカスタマイズ方法について解説しました。

このページではもう一歩踏み込んで、出力されるデータの形式を変更したり、自作のボタンを追加する方法イベントに反応して新しいウィンドウを開くといったカスタマイズ方法を解説します。


element_format

高度なカスタマイズを行うには「エディタから出力されるコンテンツを把握し必要な形に変換する」といった作業が必要になります。
そこで公式のContent Filteringページから特に重要と思われる項目を解説していきます。

このオプションでHTMLモードまたはXHTMLモードを指定することができます。
ざっくり言えば、改行の扱いが<br><br />かの違いです。

この章では出力結果を変換するため、出力の結果も見られるようにします。
具体的には以下のようにして検証を行います。

<?php
	function h($s) {
		echo htmlspecialchars($s, ENT_QUOTES, "UTF-8");
	}
	$data = @$_POST["foo"];
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="js/tinymce/tinymce.min.js"></script>
  <script>
tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  element_format : 'html'
});
  </script>
</head>
<body>
    <form method="post">
        <!-- 送信されたデータを表示 -->
        <textarea if="foo" name="foo"><?php h($data); ?></textarea>
        <input type="submit" value="送信">
    </form>

    <h2>送信データ</h2>
    <?php if ($data) { ?>
        <pre><?php h($data); ?></pre>
    <?php } ?>
</body>
</html>

element_format デモ28ページ

ちなみにTinyMCEでは改行を行うと自動で段落が作られます。そのため段落内で改行を行うには「Shift + Enter」と入力してください。

通常の出力ではxml形式なので<br />となっている。

htmlと指定すると以下のように<br>となる。


encoding

山括弧(<>)やアンド(&)がEntity処理されて渡されます。
デフォルトでは無効になっています。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  encoding: 'xml'
});

encodingのテスト デモ29ページ

通常「test」と入力した場合、以下のようになります。

<p>test</p>

xmlとすると以下のようにEntity処理されます。

&lt;p&gt;test&lt;/p&gt;

entities

Entity処理を行う文字とコードの一覧です。
奇数項目が「数値文字参照の数字部分」、偶数項目が対応する「文字実体参照の名前部分」となります。例えばノーブレークスペースの場合は「&#160;」と「&nbsp;」なので「160,nbsp」となります。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  entities : '160,,162,euro'
});

entitiesのテスト デモ30ページ

通常数値文字の&#162;¢です。つまり#&cent;です。
このデモでは「162,euro」としているので、¢を入力すると出力は&euro;となり、以下のようになります。

<p>&euro;</p>

蛇足ですが対応する文字コードを入力しないと「&;」とだけ出力されます。(上のデモで言うノーブレークスペース。つまり特定の文字を削除するといった用途には使えない)

Entity文字の一覧は「TinyMCE – entities」というページにあります。

tinyMCE.init({
        ...
        entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,'
        + '171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,'
        + '183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,'
        + '194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,'
        + '205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,'
        + '216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,'
        + '227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,'
        + '238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,'
        + '249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,'
        + '917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,'
        + '931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,'
        + '950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,'
        + '963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,'
        + '8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,'
        + '8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,'
        + '8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,'
        + '8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,'
        + '8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,'
        + '8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,'
        + '376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,'
        + '8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro'
});

entity_encoding

Entity処理の形式を指定します。

ちなみにTinyMCEがbase entitiesとする「< > & ‘ “」はこの設定のいかんにかかわらず強制的にEntity処理されます。ただし「‘ “」で囲まれた要素に限り「< >」がそのまま出力されます。

おそらく脆弱性対策なのだと思いますが、個人的にはありがた迷惑な実装だと思います。多重エスケープの原因にもなるため、入力時にはそのままで、出力時にエスケープ処理することが基本だと思います。

namedとすると文字実体参照¢であれば&cent;となる。

numericとすると数値実体参照¢であれば&#162;となる。

rawとすると生データ¢であれば¢となる。ただし「< > & ‘ “」は別。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  entity_encoding : "raw"
});

entity_encoding:namedのテスト デモ31ページ
entity_encoding:numericのテスト デモ32ページ
entity_encoding:rawのテスト デモ33ページ

それぞれ¢を入力して試してみてください。


force_p_newlines(旧式)

このオプションは削除予定ですが重要な項目のため、他の解説から拝借している場合は、以下のforced_root_blockに置き換えてください。


forced_root_block

TinyMCEでは要素を追加すると自動で新たな段落(P要素)が作成されます。
英語圏では問題ないのかもしれませんが、日本語では同じ段落で改行するというほうが多いのではないでしょうか。実は段落内での改行はShift + Enterで可能なのですが、ほとんどの利用者は知らないと思います。

そこで必要になるのがこのオプションです。
このオプションをfalseにすることで、Enterで改行になります。また、Shift + Enterで段落が作られるようになります。要は改行と段落の扱いが逆になるということです。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  forced_root_block : false
});

forced_root_blockのテスト デモ34ページ


forced_root_block_attrs

上のforced_root_blockで追加される要素の属性を決められます。段落に独自のクラスやタグを追加したいといった用途に使えます。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  forced_root_block_attrs: {
    'class': 'myclass',
    'data-something': 'my data'
  }
});

forced_root_block_attrsのテスト デモ35ページ


valid_elements

装飾された要素を渡す際に有効にする属性を指定します。
つまり、このオプションで有効でない要素は削除されます。

書式が特殊なので、Control characters:をご覧ください。

tinyMCE.init({
  selector: 'textarea',  // change this value according to your HTML
  valid_elements : 'a[href|target=_blank],strong/b,div[align],br'
});

valid_elementsのテスト デモ36ページ

このデモではa属性やstrongなどしか有効でないので、送信ボタンを押すと、斜体や右寄せといった要素は削除されるのを確認してください。


block_formats

formatselectでプルダウンした際に表示されるメニュー。
デフォルトは以下の通り。

Default Value: 'Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre'

今回は「見出し」という表記に変更します。

tinymce.init({
  selector: 'textarea',  // change this value according to your html
  toolbar: 'formatselect',
  block_formats: "Paragraph=p;見出し1=h2;見出し2=h3;見出し3=h4"
});

block_formatsのテスト デモ37ページ

このデモでは「見出し1」を選択すると「h2」で囲まれます。
ちなみにtoolでformatselectを指定しないと動作しない。(formatsの項目とは異なる)


fontsize_formats

フォントサイズのプルダウンメニューを変更する。
スペース区切りでpt、pxもしくは%を指定する。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  toolbar: 'fontsizeselect',
  fontsize_formats: '8pt 36pt'
});

fontsize_formatsのテスト デモ38ページ


formats

エディタに予め用意された書式や各種ボタンの動作を変更します。
柔軟なカスタマイズが可能な反面、要素を追加したり削除したりとなるとフォーマットの理解がかかせません。

フォーマット一覧(Built in formats)

この項目はTinyMCEで既に定義されたフォーマットの一覧。上書きすることで変更が可能となる。

alignleft
aligncenter
alignright
alignjustify
bold
italic
underline
strikethrough
forecolor
hilitecolor
fontname
fontsize
blockquote
removeformat
p
h1, h2, h3, h4, h5, h6
div
address
pre
div
code
dt, dd
samp

フォーマットパラメータ(Format parameters)

それぞれのフォーマットへ変更を加える際の仕様。
各要素の特徴を理解することが重要となる。

inline
実行した際に使われるラッパーを指定する。spanなど。インライン要素の場合。

block
実行した際に使われるラッパーを指定する。h1など。ブロック要素の場合。

selector
実行した際に影響を与える要素を指定する。p,h1,h2,h3ul,ol,liなど

classes
要素に追加されるCSSを指定する。text-align:right;など

attributes
要素に追加される属性を指定する。独自のname属性など

exact
特定のラッパーで囲まれている場合は除外するといった場合に利用する。

wrapper
特定の要素のラッパーであることを指定する。

なかなか複雑な仕組みであることを理解していただけたと思います。
そしてTinyMCEのドキュメントは往々にしてそうですが、公式のサンプルはそのままではまともに動作しません。

まずは上書きして動作を変更するデモをご覧ください。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  formats: {
    alignleft: {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', styles: {'text-align': 'right'}}
  }
});

formatsのテスト デモ39ページ

これは公式で用意されている左寄せを上書きして右寄せに変更したものです。
各フォーマットは「タイトル:{設定}」といった構成になっています。selectorで適応する要素を指定、stylesで追加するCSSを指定しています。
公式のサンプルでは「classes : ‘left’」となっていますが、これでは「class=”left”」という追記を行うだけです。(独自のCSSを読み込まない限り動作しません)

続いて新しいフォーマットを追加して利用する方法を紹介します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="js/tinymce/tinymce.min.js"></script>
  <script>
    tinymce.init({
      selector: 'textarea',
      language: "ja", // 言語 = 日本語
       formats: {
         custom_format: {block : 'h1', attributes : {title : 'Header'}, styles : {color : 'red'}}
       }
    });
  </script>
</head>
<body>
	<textarea></textarea>
	<a href="#" onclick="tinymce.activeEditor.formatter.apply('custom_format');return false;">[Apply custom format]</a>
	<a href="#" onclick="tinymce.activeEditor.formatter.remove('custom_format');return false;">[Remove custom format]</a>
</body>
</html>

文字を入力して選択した状態で[Apply custom format]をクリックすると自作したフォーマットが適用されます。[Remove custom format]で無効になります。

クリックをフラグに作成したフォーマットをオンオフしています。このデモではテキストリンクでしたが、自作のボタンやリストを追加してコールバック関数で指定すればオリジナルのボタンやリストを簡単に実装できます。

formatsのテスト2 デモ40ページ


style_formats

書式のプルダウンメニューを変更することができます。
決まった書式しか使わないような場合には便利です。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  style_formats: [
    {title: 'Bold text', inline: 'b'},
    {title: 'Red text', inline: 'span', styles: {color: '#ff0000'}},
    {title: 'Red header', block: 'h1', styles: {color: '#ff0000'}},
    {title: 'Example 1', inline: 'span', classes: 'example1'},
    {title: 'Example 2', inline: 'span', classes: 'example2'},
    {title: 'Table styles'},
    {title: 'Table row 1', selector: 'tr', classes: 'tablerow1'}
  ]
});

style_formatsのテスト2 デモ41ページ

デフォルトの値は以下の通り
What is default style_formats?


style_formats_autohide

特定のスタイルに一致する場合のみプルダウンメニューに表示される。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  plugins:"table",
  style_formats: [
    {title: 'Red cell', selector: 'td', styles: {'background-color': '#ff0000'}},
    {title: 'White text', inline: 'span', styles: {'color': '#000000'}}
  ],
  style_formats_autohide: true
});

selectorでtdとなっているので、テーブルのTable Data要素内でのみ書式に「Red cell」が表示される。

style_formats_autohideのテスト デモ42ページ


style_formats_merge

既存のメニューを活かして追加するタイプ。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  plugins:"table",
  style_formats: [
    {title: 'Red cell', selector: 'td', styles: {'background-color': '#ff0000'}},
    {title: 'White text', inline: 'span', styles: {'color': '#000000'}}
  ],
  style_formats_merge: true
});

style_formats_mergeのテスト デモ43ページ


TinyMCEのツールバーに自作のボタンを追加する方法

ボタンを追加するにはaddButton()という関数を利用します。

ちなみに公式の解説は以下のとおりです。
Create a Custom Toolbar Button

ボタンを追加する最も簡単な例

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  toolbar: ["mybutton"], // 言語 = 日本語
  setup: function(editor) { // オリジナルのプラグインボタンの追加
    editor.addButton('mybutton', {
      text: "My Button",
      onclick: function () {
         alert("My Button clicked!");
      }
    });
  } // setup: function(editor)
});

まず、setupオプションを追加します。これはエディタが読み込まれるより前に実行されるコールバック関数です。
その中でeditor.addButton()を実行しています。
第一引数のmybuttonはボタンの名前です。続いて表示のためのtext、クリックした際の動作を指定するonclickと指定します。imageでボタンアイコンのパスを指定すれば独自のアイコンも利用できます。

editor.addButtonのオプションは以下の通り

text - ボタンに表示されるテキスト
icon - アイコンのスタイルシート
image - アイコンのイメージ(16×16px、icon等ある場合はこの要素で上書き)
tooltip - ポップアップ時に表示される
onclick - ボタンをクリックした際のコールバック関数
onpostrender - ボタンがレンダリングされた際のコールバック関数
cmd - クリックした際に実行されるコマンド。(事前にコマンドを登録しておく必要あり)

注意点として、作成したボタンはtoolbarの項目で指定する必要があります。

以下のデモで、My ButtonというボタンをクリックするとMy Button clicked!というアラートが表示されます。

My Buttonのテスト デモ44ページ

エディタに何かの要素を追加するにはeditor.insertContent()を利用します。簡単なものならonclickの関数内でそのまま記述する方法もあります。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  toolbar: ["mybutton"], // 言語 = 日本語
  setup: function(editor) { // オリジナルのプラグインボタンの追加
    editor.addButton('mybutton', {
      text: "My Button",
      onclick: function () {
         editor.insertContent("hoge");
      }
    });
  } // setup: function(editor)
});

editor.insertContent()のテスト デモ45ページ

ボタンをクリックすると「hoge」と入力されます。

トグルボタン

トグルボタンとはクリックするごとにオンオフされるボタンです。例えばボールドやイタリックといった機能です。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  toolbar: ["strikethrough"], // 言語 = 日本語
  setup: function(editor) { // オリジナルのプラグインボタンの追加
    editor.addButton('strikethrough', {
      onclick: function () {
         editor.execCommand('mceToggleFormat', false, 'strikethrough');
      }
    });
  }
});

トグルボタンのテスト デモ46ページ

onclickのコールバックでexecCommand()を実行します。第一引数にmceToggleFormatを指定するとトグルボタンになります。
strikethroughは公式でサポートされてる打ち消し線のformatsです。ボタンの定義がされているためtext等の設定は必要ありません。
もちろんオリジナルのformatsも利用できます。

ボタンのオンオフを視覚的に実現するには以下のようにします。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  toolbar: ["strikethrough"], // 言語 = 日本語
  setup: function(editor) { // オリジナルのプラグインボタンの追加
    editor.addButton('strikethrough', {
      onclick: function () {
         editor.execCommand('mceToggleFormat', false, 'strikethrough');
      }
    });
  }, // setup: function(editor)
  onpostrender: function() {
    var btn = this;
    editor.on('init', function() {
      editor.formatter.formatChanged('strikethrough', function(state) {
        btn.active(state);
      });
    });
  } // onpostrender: function()
});

トグルボタンのテスト2 デモ47ページ

onpostrenderでボタンのレンダリング前にボタンの定義を変更します。
editor.on(‘init’, function(){~});の部分はエディタの初期設定へのフックです。
editor.formatter.formatChanged(‘strikethrough’, function(){~});の部分はformatterというAPIで、トグルボタン専用の定義です。formatChangedの第一引数に一致するフォーマットに状態を示すstateを渡します。
formatterに関する公式の解説

APIが出てきたので難しく感じるかもしれませんが、このへんは覚えるしかないテンプレです。必要な時にサンプルから拝借しましょう。


追加したボタンをクリックすると入力ウィンドウを開く

ここからは私がPukiWikiにTinyMCEを導入するにあたり使用したサンプルです。
実際にはどうやって利用するかの参考になれば幸いです。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="js/tinymce/tinymce.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script>
  tinymce.init({
    selector: 'textarea',
    language: "ja", // 言語 = 日本語
    toolbar: ["plugin"], // 言語 = 日本語
    setup: function(editor) { // オリジナルのプラグインボタンの追加

      editor.addButton("plugin", { // プラグインボタンの追加
        icon: "plugin",
        image: "./plugin.png",
        tooltip: "プラグイン",
        onclick: pluginWin, // コールバック関数

        onPostRender: function() { // プラグイン要素選択時プラグインボタンアクティブ
          var _this = this;
          editor.on("NodeChange", function(e) {
            var is_active = jQuery( editor.selection.getNode() ).hasClass("ref");
            var is_active = jQuery( editor.selection.getNode() ).hasClass("plugin");
            _this.active( is_active );
          })

          editor.on("DblClick", function(e) {
            if ( e.target.className == "plugin" || e.target.className=="ref" ) {
              pluginWin(e.toElement.innerText);
            }
          })
        },
      });

      function pluginWin(e) { // プラグインウィンドウを開く関数
        if (String(e).match(/^(&|#)/)) { // eはボタンをクリックした場合オブジェクト、プラグインのコンテンツをダブルクリックした際は文字列が入るので判定のため文字列に一旦変換する
          var m = e.match(/^(&|#)(\w+)\((.*)\)([\s\S]*)?/); // プラグインの形式に対応する正規表現(暫定)。ややこしいことにインラインテキストのある無しや{}を二重にしたり、改行の有無最後のセミコロンなしでも動くプラグインがあるなど対応が複雑
          if(typeof(m[4]) !== "undefined"){
            if (m[4].match(/;$/)) { m[4] = m[4].replace(/;$/g, "") }; // インライン要素のあるなしにかかわらず行末の;は削除
          }
          var inline_block_value = (m[1] === "&") ? "inline" : "block";
        } else {
          var m = new Array("", "", "", "", ""); // ボタンをクリックした際は空のデフォルト値を指定
        }
        tinymce.activeEditor.windowManager.open({
          title: "プラグイン",
          width: 600,
          height: 300,
          body: [{
            type: "listbox",
            name: "inline_block",
            label: "タイプ",
            values : [
              { text: "インライン型", value: "inline" },
              { text: "ブロック型", value: "block" }
            ],
            value: inline_block_value // valueの指定でデフォルト値を指定する
          },{
            type: "textbox",
            name: "plugin_name",
            label: "プラグイン名",
            value: m[2]
          },{
            type: "textbox", // 引数の数はプラグインの種類によって異なるためまとめて一つにする
            name: "plugin_option",
            label: "オプション",
            value: m[3]
          },{
            type: "textbox",
            name: "inline_text",
            multiline: true,
            minHeight: 150,
            label: "インライン要素({}必須)",
            value: m[4]
          }],

          onsubmit: function(e) { // OKボタンをクリックした際の挙動
            if (e.data.plugin_name === "") { // 入力値のバリデーション(簡易版)
              tinyMCE.activeEditor.windowManager.alert("プラグイン名が入力されていません。");
              return;
            }
            if (e.data.inline_block === "inline") { var inline_block = ["&", ";"] } else { var inline_block = ["#", ""] }
            if (e.data.inline_text) { var inline_text = e.data.inline_text; inline_text = inline_text.replace(/\r?\n/g, "<br>"); } else { var inline_text = ""}
            editor.insertContent( '<span class="plugin" contenteditable="false" style="cursor: default;" data-mce-style="cursor: default;">' + inline_block[0]  + e.data.plugin_name + '(' + e.data.plugin_option + ')' + inline_text + inline_block[1] + '</span>');
          }
        });
      } // function pluginWin()
    } // setup: function(editor)
  });
  </script>
</head>
<body>
	<textarea></textarea>
</body>
</html>

自作ボタンのテスト デモ48ページ

基本的には今までの解説を組み合わせたものです。

プラグインというオリジナルのボタンを追加しました。ボタンをクリックすると独自のウィンドウが開きます。

新しいのは、windowManagerというAPIを用いて入力ウィンドウを表示する点です。他にも、jQueryを利用したり、コールバック関数に独自の関数を利用しています。

追加される要素はpluginというclassを持ち、contenteditable属性がfalseのため編集不可となっています。
代わりに追加された要素をダブルクリックするとプラグインボタンをクリックしたときと同じようにウィンドウが開き、入力した値がウィンドウに反映されます。

プラグイン要素を選択中は、トグルボタンのようにプラグインボタンが押された状態になります。ここではeditor.onで「NodeChange」というオプションを利用しています。これはエディター内で選択する範囲が変更された際に実行されます。これをフラグにpluginというクラスを選択した際にボタン要素をactiveに変更しています。

イベントについては詳しく触れていませんが、Editor Eventsには様々なフラグが用意されています。ほとんどの場合はこのAPIを利用すれば目的の実装が可能だと思います。

さらに詳しくAPIについて学びたいという方は公式のAPIページをご覧ください。


自作のプラグインを作る方法

上の例を見てもらえば分かる通り、要素が複雑になるとコードも長くなります。そんな時に有効なのが自作プラグインです。

自作プラグインの作成方法は「Create a Plugin for TinyMCE」で解説されています。

このページでは「hoge」というプラグインを作成してみます。
仕組みは簡単で「js/tinymce/plugins」というフォルダにプラグイン名のフォルダを追加すると、それがそのままプラグイン名になります。つまり「hoge」というプラグインを作りたい場合は「js/tinymce/plugins/hoge」というフォルダを作ります。

そのなかに「plugin.min.js」というファイルを作ります。(公式の解説ではplugin.jsという名前でも動作すると解説されていますが、動作しません)

中身はtinymce.PluginManager.add()という関数を作り、プラグイン名の「hoge」を第一引数に指定すれば、tinymce.init()のsetupオプションで設定したコールバック関数と同じように使用できます。
今回は上のプラグインというオリジナルボタンを追加したコードをプラグイン化してみます。

tinymce.PluginManager.add('hoge', function(editor, url) {
  editor.addButton("plugin", { // プラグインボタンの追加
    icon: "plugin",
    image: "./plugin.png",
    tooltip: "プラグイン",
    onclick: pluginWin, // コールバック関数

    onPostRender: function() { // プラグイン要素選択時プラグインボタンアクティブ
      var _this = this;
      editor.on("NodeChange", function(e) {
        var is_active = jQuery( editor.selection.getNode() ).hasClass("ref");
        var is_active = jQuery( editor.selection.getNode() ).hasClass("plugin");
        _this.active( is_active );
      })

      editor.on("DblClick", function(e) {
        if ( e.target.className == "plugin" || e.target.className=="ref" ) {
          pluginWin(e.toElement.innerText);
        }
      })
    },
  });

  function pluginWin(e) { // プラグインウィンドウを開く関数
    if (String(e).match(/^(&|#)/)) { // eはボタンをクリックした場合オブジェクト、プラグインのコンテンツをダブルクリックした際は文字列が入るので判定のため文字列に一旦変換する
      var m = e.match(/^(&|#)(\w+)\((.*)\)([\s\S]*)?/); // プラグインの形式に対応する正規表現(暫定)。ややこしいことにインラインテキストのある無しや{}を二重にしたり、改行の有無最後のセミコロンなしでも動くプラグインがあるなど対応が複雑
      if(typeof(m[4]) !== "undefined"){
        if (m[4].match(/;$/)) { m[4] = m[4].replace(/;$/g, "") }; // インライン要素のあるなしにかかわらず行末の;は削除
      }
      var inline_block_value = (m[1] === "&") ? "inline" : "block";
    } else {
      var m = new Array("", "", "", "", ""); // ボタンをクリックした際は空のデフォルト値を指定
    }
    tinymce.activeEditor.windowManager.open({
      title: "プラグイン",
      width: 600,
      height: 300,
      body: [{
        type: "listbox",
        name: "inline_block",
        label: "タイプ",
        values : [
          { text: "インライン型", value: "inline" },
          { text: "ブロック型", value: "block" }
        ],
        value: inline_block_value // valueの指定でデフォルト値を指定する
      },{
        type: "textbox",
        name: "plugin_name",
        label: "プラグイン名",
        value: m[2]
      },{
        type: "textbox", // 引数の数はプラグインの種類によって異なるためまとめて一つにする
        name: "plugin_option",
        label: "オプション",
        value: m[3]
      },{
        type: "textbox",
        name: "inline_text",
        multiline: true,
        minHeight: 150,
        label: "インライン要素({}必須)",
        value: m[4]
      }],

      onsubmit: function(e) { // OKボタンをクリックした際の挙動
        if (e.data.plugin_name === "") { // 入力値のバリデーション(簡易版)
          tinyMCE.activeEditor.windowManager.alert("プラグイン名が入力されていません。");
          return;
        }
        if (e.data.inline_block === "inline") { var inline_block = ["&", ";"] } else { var inline_block = ["#", ""] }
        if (e.data.inline_text) { var inline_text = e.data.inline_text; inline_text = inline_text.replace(/\r?\n/g, "<br>"); } else { var inline_text = ""}
        editor.insertContent( '<span class="plugin" contenteditable="false" style="cursor: default;" data-mce-style="cursor: default;">' + inline_block[0]  + e.data.plugin_name + '(' + e.data.plugin_option + ')' + inline_text + inline_block[1] + '</span>');
      }
    });
  } // function pluginWin()
});

これで「hogeプラグイン」が完成しました。
あとは他のプラグインと同じようにpluginsに「hoge」を追加して、ツールバーへの追加のために「plugin」と記述するだけです。

tinymce.init({
  selector: 'textarea',
  language: "ja", // 言語 = 日本語
  plugins: 'hoge',
  toolbar: 'plugin'
});

自作プラグインのテスト デモ49ページ

これでツールバーにオリジナルボタンを追加しつつ、コード自体は短くすることができます。
javaScriptを圧縮したり、配布可能な形式にしたりするTinyMCE plugin Yeoman generatorという専用のツールも存在するようです。配布予定の方は利用してください。


現在のページを共有する



現在のページに関連する記事

公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) 公式ドキュメントよりも詳しいTinyMCEの使い方(基本編)
公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) Firefox機能拡張のポップアップとコンテンツスクリプトについて
公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) 5段階評価プラグインを通して学ぶPukiWikiのプラグインを作成する方法
公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) Google Feed APIの代替手段としてjQueryだけでRSSを表示する方法
公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) PukiWikiをビジュアルエディタ(TinyMCE)で編集できるプラグインを作成
PukiWikiで複数行のコメントを挿入 PukiWikiで複数行のコメントを挿入
公式ドキュメントよりも詳しいTinyMCEの使い方(応用編) 人気の画像掲示板「JoyfulNote」でスパム対策を追加

おすすめの記事

fluentdとNorikraでDoS攻撃を遮断し、メールで通知する方法

fluentdとNorikraでDoS攻撃を遮断し、メールで通知する方法

Apacheのmod_expiresでファイルタイプごとにキャッシュをコントロールする方法

Apacheのmod_expiresでファイルタイプごとにキャッシュをコント…

Windows 10でpsd形式のファイルをサムネイル表示する方法

Windows 10でpsd形式のファイルをサムネイル表示する方法

fluentdと連動して集計処理を行うNorikraの導入方法

fluentdと連動して集計処理を行うNorikraの導入方法

作業効率を飛躍的に向上させる!実務で使うIllustratorのショートカット一覧

作業効率を飛躍的に向上させる!実務で使うIllustratorのショー…

DELL Inspiron 3250のHDDをSSDへ換装して、メモリを16GBへ増設

DELL Inspiron 3250のHDDをSSDへ換装して、メモリを16GBへ増設

シンプルなスパムコメント対策プラグイン「Simple AntiSpam」を作成しました

シンプルなスパムコメント対策プラグイン「Simple AntiSpam」を…

iptablesの設定ファイルをシェルスクリプトを利用して動的に作成

iptablesの設定ファイルをシェルスクリプトを利用して動的に作成


いただいたコメントなど

  1. なつやすみ のコメント:

    こちらの記事のおかげで、2日間困っていたことが解決しました。
    たすかりました。ありがとうございました。

コメントを残す

コメントは認証制のため、すぐには反映されません。

プログラミングに関する質問は「日本語でプログラミングの悩みを解決するQ&Aサイト sukegra」をご利用ください。