CKEditor 4のプラグインを書く

CKEditorは、ブラウザー用のWYSIWYG HTMLエディターです。 ドキュメントやソースコード、プラグインのソースコードに出くわすたびに、迷子になりました。 CKEditorはかなり複雑なインフラストラクチャを備えた非常に大きな製品であるため、これは驚くことではありません。 ただし、多くの場合、標準機能では十分ではないため、独自の機能を追加する必要があります。 この記事では、エディターでYandexカードを埋め込み、操作できるプラグインについて詳しく説明します。



これは、編集の終わりにどのように見えるかです:





そして編集モードで:





動作原理



だから。 将来のツールがどのように動作するかを判断しましょう。 カードをドキュメントに埋め込み、そこから削除し、パラメータを変更できることが必要です。 なぜなら HTML編集モードで<script />を操作することは、少なくとも簡単な作業ではありません。標準のfakeobjectsプラグインを使用します。これにより、HTMLを当面より便利なもの、より正確には<img /> (それらを使用するのは偽オブジェクトです)。 とりわけ、マップ設定を編集するには「ダイアログ」プラグインが必要です。 プラグインは、かなり原始的なものになることがすぐにわかります。 複雑なことをする仕事はありませんでした。 プラグインを使用すると、2つの座標(緯度と経度)、スケール(1-17)の値を使用してマップを配置でき、マップの下に説明付きのテキストと同様に、マークを配置できます。



なぜなら Yandexカードは動的に接続され、不必要な<script />を埋め込むオブジェクトです-s、恩恵のないタスクの有無にかかわらず、次のHTMLコードを出力で受け取ります:<em data-plugin = "-json_data-"> MAP < / em>。 そして、簡単なjavascript関数の助けを借りて、YandexMap APIを使用して<em /> キャリッジをカードに変換します。



多くの点で標準プラグイン「Flash」の作業に依存していたため、使用するものの一部が完全に理解されていないことに注意してください。 大部分は不完全なドキュメントによるもので、一部は私の理解不足によるものです。 ソースコードに基づいて結論の大部分を作成し、 StackOverflowの応答データベースの一部を作成しました。



プラグインの作成



便宜上、すべてのプラグインコードを自己実行匿名関数内に配置します。

function() { /* code */ } )();
      
      







システムにプラグインを追加するには、次を使用します。

 CKEDITOR.plugins.add( 'ymap' /*   */, //      { //  requires: [ 'dialog', 'fakeobjects' ], //      init: function( editor /*   -   */ ) { }, //     ,      API afterInit: function( editor ) { } } );
      
      







initの詳細:

 this._initTranslations( editor ); // ,    var req = 'em' /* tag */ + '[!data-plugin,!width,!height]' /* attrs */ + '(' + ymap_class_name + ')' /* classes */ + '{width,height}' /* styles */; //      editor.addCommand( 'ymap', new CKEDITOR.dialogCommand( 'ymap', { allowedContent: req } ) ); //      editor.ui.addButton( 'YMap', { label: lang.button_label, command: 'ymap' } ); //    CKEDITOR.dialog.add( 'ymap', add_dialog /* ,    */ ); //      - //     fakeobject -    editor.on( 'doubleclick', function( evt ) { var element = evt.data.element; if( element.is('img') && element.data('cke-real-element-type') == 'ymap' ) { evt.data.dialog = 'ymap'; } } );
      
      





最も重要な点は、新しいチームを作成するときのallowedContentの定義です。 実際、CKEditor 4では、「許可コンテンツルール」という新しいシステムが追加されました。 このシステムは、不正なHTMLコード(たとえば、外部から貼り付ける場合)と戦います。 HTMLコードから余分なタグ、属性、スタイル、およびクラスを削除します。 そして、システムに不必要なものとそうでないものを明確にするために、コマンドを登録するときに、 allowedContentフィールドとrequiredContentフィールドを含むことができるオブジェクトを指定します。 最初のタスクは機会を要求することであり、2番目のタスクは機会が十分でない場合にコマンドを無効にすることです。 allowedContentのみを使用すれば十分でした。 その仕事の原則はここ見つけることができます 。 最も基本的なことに焦点を当てます。



ローカライズの別の瞬間。 あなたは好きなようにそれを自由に整理することができます;私は次のアプローチに落ち着きました:

 var translations = { ru: { fake_object_label: 'Yandex ', title: 'Yandex ', // ... }, en: { fake_object_label: 'Yandex Map', title: 'Yandex Map', // ... }, def: 'ru' }; var lang; // shotrcut // ... CKEDITOR.plugins.add( 'ymap', { // ... _initTranslations: function( editor ) { var current_lang = CKEDITOR.lang.detect(); CKEDITOR.lang[ current_lang ]['ymap'] = translations[ current_lang ] ? translations[ current_lang ] : translations[ translations.def_lang ]; lang = editor.lang.ymap; //      fakeobject editor.lang.fakeobjects.ymap = lang.fake_object_label; }, // ... } );
      
      







afterInit



この関数のコードでは、エディターのフィルターに独自のフィルターを追加します。これにより、ソースコードモードから(またはエディターが初期化された直後に)編集モードに切り替えると、実際のコードをfakeobjectに置き換えることができます。



 afterInit: function( editor ) { //     var dataProcessor = editor.dataProcessor, dataFilter = dataProcessor && dataProcessor.dataFilter; if( dataFilter ) { //     dataFilter.addRules ( { //   elements: { //       em.    //   ,    inline-,  //   CKEditor-     //  P- 'em': function( el ) { //   em -  Ymap,    if( ! is_plugin_node( el ) ) { for( var i = 0 ; i < el.children.length; ++ i ) { if( el.children[ i ].name === 'cke:ymap' ) { if( ! is_plugin_node( el.children[ i ] ) ) { return null; //    } //   fakeobject return create_fake_element( editor, el ); } } return null; //    } //  -   fakeobject return create_fake_element( editor, el ); } } }, 1 /*   */ ) } // if dataFilter }
      
      







アイテムが「マップ」であるかどうかを確認する機能

 var is_plugin_node = function( el ) { return el.attributes['class'] === ymap_class_name; };
      
      







emを偽オブジェクトに変換する関数:

 var create_fake_element = function( editor, real_el ) { //  ,   IMG,  ,  return editor.createFakeParserElement( real_el, 'cke_ymap', 'ymap', true ); };
      
      





伸縮性により、オブジェクトが幅と高さを持つ可能性を理解する必要があります。 falseの場合、fakeobjectは元の要素の幅と高さを継承しません。



対話



だから私たちは最も重要なこと-対話に来ます。 ほとんどすべてがそこにあります。 次のように説明されています。

 var add_dialog = function( editor ) { var dialog = { title: lang.title, //   //    width: 300, height: 100, //    —  , ,  … contents: [ ], //  onShow: function(){}, onOk: function(){} }; return dialog; };
      
      







さて、順番に。 コンポーネントから始めましょう:

 { // , ,   1,     id: 'tab_info', expand: true, padding: 0, elements: [ { //  id: 'name', type: 'text', label: lang.f_name, commit: commit, setup: load }, { //  id: 'label', type: 'text', label: lang.f_label, commit: commit, setup: load }, { // . type: 'hbox', align: 'left', children: [ { // latitude id: 'lon', type: 'text', label: lang.f_lat, commit: commit, validate: CKEDITOR.dialog.validate .regex( /^[\d\,\.]+$/, lang.inc_coord ), setup: load, 'default': '43.2503' }, // …
      
      







コンポーネントに次のハンドラーを設定できます。



idフィールドは、属性としてではなく、コンポーネントの識別子として使用されます。 CKEDITOR.dialog.validateオブジェクトで、すべてのタイプの既製のバリデーターを確認できます。



ショー



ご想像のとおり、このメソッドはダイアログが表示されたときに呼び出されます。 その中でのタスクは、呼び出しの前にfakeobjectが強調表示されている場合、すべての準備を行い、フィールドの値を入力することです。



 //     this.fake_image = this.ymap_node = null; //     -     var fake_image = this.getSelectedElement(); //    ... if( fake_image && fake_image.data( 'cke-real-element-type' ) === 'ymap' ) { this.fake_image = fake_image; //     EM- (   ,   //  ) this.ymap_node = editor.restoreRealElement( fake_image ); // ..       JSON   "data-plugin", //       cfg var cfg = JSON.parse( this.ymap_node.getAttribute('data-plugin') ) //    setup    . //  setup-    ,    // .     cfg this.setupContent( cfg ); }
      
      







セットアップ機能(コンポーネント用):

 var load = function( cfg ) { //      -   cfg //       data-plugin  //      (   :) ) this.setValue( cfg[ '_' + this.id ] ); };
      
      







オノク



この関数は、ユーザーがダイアログの「OK」ボタンをクリックしたときに呼び出され、すべてのフィールドにエラーが含まれていませんでした。 すべての機能の中で最も重要なのは、その中でfakeobjectを作成および操作し、最終的なEM-kuを作成することです。

 // this.fake_image    onShow,  //     fakeobject //   ,          //      if( ! this.fake_image ) { //   EM- var node = CKEDITOR.dom.element .createFromHtml( '<cke:em>MAP</cke:em>', editor.document ); //      node.addClass( ymap_class_name ); } else { //      //    EM-  , //     onShow //  restoreRealElement var node = this.ymap_node; } //      var extra_styles = {}, extra_attributes = {}; //    commit      //       load. //       ,    //     this.commitContent( node, extra_styles, extra_attributes ); //    EM- node.setStyles( extra_styles ); //   -      node.$.setAttribute( 'data-plugin', JSON.stringify( extra_attributes ) ); //   fakeobject- //   - ,     //   - ,     IMG //   -    //  -          var new_fake_image = editor.createFakeElement( node, 'cke_ymap', 'ymap', true ); new_fake_image.setAttributes( extra_attributes ); new_fake_image.setStyles( extra_styles ); //      fakeobject if( this.fake_image ) { //    new_fake_image.replace( this.fake_image ); //   editor.getSelection().selectElement( new_fake_image ); } else { //       //     -  //     editor.insertElement( new_fake_image ); }
      
      







コンポーネントのコミット機能(commitContentを使用して強制的に呼び出します):

 var commit = function( ymap, styles, attrs ) { var value = this.getValue(); if( this.id === 'width' || this.id === 'height' ) { //       //       styles[ this.id ] = value; } else if( this.id === 'lat' || this.id === 'lon' ) { //      -     value = value.replace( ',', '.' ); } //     , //    onOk     data-plugin attrs[ '_' + this.id ] = value; };
      
      







オブジェクトに幅と高さのフィールドがあると想定される場合、それらを使用する必要があることに注意してください。



CSS





CSSでは、EMおよびIMG fakeobjectの外観を設定する必要があります。 EMの外観はサイトのスタイルで設定され、IMGの外観はエディターに接続するスタイルで設定されます。 また、JSを使用して設定することもできます。

 CKEDITOR.addCss( 'img.cke_ymap { /* css */ }' );
      
      







まとめ



プラグイン全体は複雑ではありませんでした。 この理由は、fakeobjectプラグインの存在と、1つのブロックオブジェクトで作業するという事実です。 ユーザーがテキストを選択して、異なるタグのコンテンツの異なる部分を入力できるため、小文字タグの操作ははるかに複雑になると思います。 しかし、私は試していません:)



必要に応じて、コンテキストメニューをプラグインに接続し、フィールド(たとえば、1つだけでなく多数のラベルを指定できる)、プレビューパネルなどを追加できます。



プラグインはここ (github)にあります。 リポジトリには、YandexMapsへの接続例も含まれています。 しかし、実際には、他のオンラインカードサービスを使用できます。



参照資料






All Articles