整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          CKEditor系列(五)編輯器內容的設置和獲取過程

          們看一下CKEditor4的編輯器內容的設置和獲取過程,也就是setData和getData過程。

          我們在調用editor.setData的時候,調用的就是core/editor.js里面的setData方法。

          // src/core/editor.js
          setData: function( data, options, internal ) {
              var fireSnapshot = true,
                  // Backward compatibility.
                  callback = options,
                  eventData;
          
              if ( options && typeof options == 'object' ) {
                  internal = options.internal;
                  callback = options.callback;
                  fireSnapshot = !options.noSnapshot;
              }
          
              if ( !internal && fireSnapshot )
                  this.fire( 'saveSnapshot' );
          
              if ( callback || !internal ) {
                  this.once( 'dataReady', function( evt ) {
                      if ( !internal && fireSnapshot )
                          this.fire( 'saveSnapshot' );
          
                      if ( callback )
                          callback.call( evt.editor );
                  } );
              }
          
              // Fire "setData" so data manipulation may happen.
              eventData = { dataValue: data };
              !internal && this.fire( 'setData', eventData );
          
              this._.data = eventData.dataValue;
          
              !internal && this.fire( 'afterSetData', eventData );
          },

          我們可以看到里面的set過程實際是分三步

          1. 判斷是否需要saveSnapshot
          2. 判斷是否需要觸發setData事件
          3. 判斷是否需要觸發afterSetData事件

          setData之saveSnapshot

          saveSnapshot主要是方便撤銷操作的

          // src/plugins/undo.plugin.js
          // Save snapshots before doing custom changes.
          editor.on( 'saveSnapshot', function( evt ) {
              undoManager.save( evt.data && evt.data.contentOnly );
          } );

          setData之setData

          我們接著看setData事件的處理

          src/core/section.js
          editor.on( 'setData', function() {
              // Invalidate locked selection when unloading DOM.
              // (https://dev.ckeditor.com/ticket/9521, https://dev.ckeditor.com/ticket/5217#comment:32 and https://dev.ckeditor.com/ticket/11500#comment:11)
              editor.unlockSelection();
          
              // Webkit's selection will mess up after the data loading.
              if ( CKEDITOR.env.webkit )
                  clearSelection();
          } );

          我們可以看到,它做的工作主要是解鎖選區,看來實際做工作的還不是setData啊,它算是一個setData的準備工作可能更合適些。

          setData之afterSetData

          // src/core/editable.js
          this.attachListener( editor, 'afterSetData', function() {
              this.setData( editor.getData( 1 ) );
          }, this );

          沒錯,這里又有個一個setDatagetData。。。原來他們才是真正的setDatagetData啊。

          // src/core/editable.js
          /**
           * @see CKEDITOR.editor#setData
           */
          setData: function( data, isSnapshot ) {
              if ( !isSnapshot )
                  data = this.editor.dataProcessor.toHtml( data );
          
              this.setHtml( data );
              this.fixInitialSelection();
          
              // Editable is ready after first setData.
              if ( this.status == 'unloaded' )
                  this.status = 'ready';
          
              this.editor.fire( 'dataReady' );
          },
          
          /**
           * @see CKEDITOR.editor#getData
           */
          getData: function( isSnapshot ) {
              var data = this.getHtml();
          
              if ( !isSnapshot )
                  data = this.editor.dataProcessor.toDataFormat( data );
          
              return data;
          },

          setHtmlgetHtml本質就是原生node的innerHTML,所以setDatagetData的過程其實就是 this.editor.dataProcessor.toHtmlthis.editor.dataProcessor.toDataFormat的過程,這個兩個方法哪來的?它們都源自dataProcessor,它是在編輯器初始化的時候賦值的。

          dataProcessor

          // src/core/editor.js
          // Various other core components that read editor configuration.
          function initComponents( editor ) {
              // Documented in dataprocessor.js.
              editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor );
          
              // Set activeFilter directly to avoid firing event.
              editor.filter = editor.activeFilter = new CKEDITOR.filter( editor );
          
              loadSkin( editor );
          }

          dataProcessor的兩個具體方法如下

          // src/core/dataProcessor.js
          toHtml: function( data, options, fixForBody, dontFilter ) {
              var editor = this.editor,
                  context, filter, enterMode, protectedWhitespaces;
          
              // Typeof null == 'object', so check truthiness of options too.
              if ( options && typeof options == 'object' ) {
                  context = options.context;
                  fixForBody = options.fixForBody;
                  dontFilter = options.dontFilter;
                  filter = options.filter;
                  enterMode = options.enterMode;
                  protectedWhitespaces = options.protectedWhitespaces;
              }
              // Backward compatibility. Since CKEDITOR 4.3.0 every option was a separate argument.
              else {
                  context = options;
              }
          
              // Fall back to the editable as context if not specified.
              if ( !context && context !== null )
                  context = editor.editable().getName();
          
              return editor.fire( 'toHtml', {
                  dataValue: data,
                  context: context,
                  fixForBody: fixForBody,
                  dontFilter: dontFilter,
                  filter: filter || editor.filter,
                  enterMode: enterMode || editor.enterMode,
                  protectedWhitespaces: protectedWhitespaces
              } ).dataValue;
          },
          toDataFormat: function( html, options ) {
              var context, filter, enterMode;
          
              // Do not shorten this to `options && options.xxx`, because
              // falsy `options` will be passed instead of undefined.
              if ( options ) {
                  context = options.context;
                  filter = options.filter;
                  enterMode = options.enterMode;
              }
          
              // Fall back to the editable as context if not specified.
              if ( !context && context !== null )
                  context = this.editor.editable().getName();
          
              return this.editor.fire( 'toDataFormat', {
                  dataValue: html,
                  filter: filter || this.editor.filter,
                  context: context,
                  enterMode: enterMode || this.editor.enterMode
              } ).dataValue;
          },

          這兩個方法的具體實現被化成對兩個(toHtmltoDataFormat)事件的處理邏輯了。

          dataProcessor之toHtml

          這兩個事件有哪些回調呢,先看toHtml

          // src/core/dataProcessor.js
          editor.on( 'toHtml', function( evt ) {
              var evtData = evt.data,
              data = evtData.dataValue,
              fixBodyTag;
          
              // Before we start protecting markup, make sure there are no externally injected
              // protection keywords.
              data = removeReservedKeywords( data );
          
              // The source data is already HTML, but we need to clean
              // it up and apply the filter.
              data = protectSource( data, editor );
          
              // Protect content of textareas. (https://dev.ckeditor.com/ticket/9995)
              // Do this before protecting attributes to avoid breaking:
              // <textarea><img src="..." /></textarea>
              data = protectElements( data, protectTextareaRegex );
          
              // Before anything, we must protect the URL attributes as the
              // browser may changing them when setting the innerHTML later in
              // the code.
              data = protectAttributes( data );
          
              // Protect elements than can't be set inside a DIV. E.g. IE removes
              // style tags from innerHTML. (https://dev.ckeditor.com/ticket/3710)
              data = protectElements( data, protectElementsRegex );
          
              // Certain elements has problem to go through DOM operation, protect
              // them by prefixing 'cke' namespace. (https://dev.ckeditor.com/ticket/3591)
              data = protectElementsNames( data );
          
              // All none-IE browsers ignore self-closed custom elements,
              // protecting them into open-close. (https://dev.ckeditor.com/ticket/3591)
              data = protectSelfClosingElements( data );
          
              // Compensate one leading line break after <pre> open as browsers
              // eat it up. (https://dev.ckeditor.com/ticket/5789)
              data = protectPreFormatted( data );
          
              // There are attributes which may execute JavaScript code inside fixBin.
              // Encode them greedily. They will be unprotected right after getting HTML from fixBin. (https://dev.ckeditor.com/ticket/10)
              data = protectInsecureAttributes( data );
          
              var fixBin = evtData.context || editor.editable().getName(),
                  isPre;
          
              // Old IEs loose formats when load html into <pre>.
              if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && fixBin == 'pre' ) {
                  fixBin = 'div';
                  data = '<pre>' + data + '</pre>';
                  isPre = 1;
              }
          
              // Call the browser to help us fixing a possibly invalid HTML
              // structure.
              var el = editor.document.createElement( fixBin );
              // Add fake character to workaround IE comments bug. (https://dev.ckeditor.com/ticket/3801)
              el.setHtml( 'a' + data );
              data = el.getHtml().substr( 1 );
          
              // Restore shortly protected attribute names.
              data = data.replace( new RegExp( 'data-cke-' + CKEDITOR.rnd + '-', 'ig' ), '' );
          
              isPre && ( data = data.replace( /^<pre>|<\/pre>$/gi, '' ) );
          
              // Unprotect "some" of the protected elements at this point.
              data = unprotectElementNames( data );
          
              data = unprotectElements( data );
          
              // Restore the comments that have been protected, in this way they
              // can be properly filtered.
              data = unprotectRealComments( data );
          
              if ( evtData.fixForBody === false ) {
                  fixBodyTag = false;
              } else {
                  fixBodyTag = getFixBodyTag( evtData.enterMode, editor.config.autoParagraph );
              }
          
              // Now use our parser to make further fixes to the structure, as
              // well as apply the filter.
              data = CKEDITOR.htmlParser.fragment.fromHtml( data, evtData.context, fixBodyTag );
          
              // The empty root element needs to be fixed by adding 'p' or 'div' into it.
              // This avoids the need to create that element on the first focus (https://dev.ckeditor.com/ticket/12630).
              if ( fixBodyTag ) {
                  fixEmptyRoot( data, fixBodyTag );
              }
          
              evtData.dataValue = data;
          }, null, null, 5 );
          
          // Filter incoming "data".
          // Add element filter before htmlDataProcessor.dataFilter when purifying input data to correct html.
          editor.on( 'toHtml', function( evt ) {
              if ( evt.data.filter.applyTo( evt.data.dataValue, true, evt.data.dontFilter, evt.data.enterMode ) )
                  editor.fire( 'dataFiltered' );
          }, null, null, 6 );
          
          editor.on( 'toHtml', function( evt ) {
              evt.data.dataValue.filterChildren( that.dataFilter, true );
          }, null, null, 10 );
          
          editor.on( 'toHtml', function( evt ) {
              var evtData = evt.data,
                  data = evtData.dataValue,
                  writer = new CKEDITOR.htmlParser.basicWriter();
          
              data.writeChildrenHtml( writer );
              data = writer.getHtml( true );
          
              // Protect the real comments again.
              evtData.dataValue = protectRealComments( data );
          }, null, null, 15 );

          我們可以看到這些回調里面最多的幾個單詞就是protectfilter,它們主要也是做這些工作。

          dataProcessor之toDataFormat

          再看看toDataFormat的回調

          // src/core/dataProcessor.js
          editor.on( 'toDataFormat', function( evt ) {
              var data = evt.data.dataValue;
          
              // https://dev.ckeditor.com/ticket/10854 - we need to strip leading blockless <br> which FF adds
              // automatically when editable contains only non-editable content.
              // We do that for every browser (so it's a constant behavior) and
              // not in BR mode, in which chance of valid leading blockless <br> is higher.
              if ( evt.data.enterMode != CKEDITOR.ENTER_BR )
                  data = data.replace( /^<br *\/?>/i, '' );
          
              evt.data.dataValue = CKEDITOR.htmlParser.fragment.fromHtml(
                  data, evt.data.context, getFixBodyTag( evt.data.enterMode, editor.config.autoParagraph ) );
          }, null, null, 5 );
          
          editor.on( 'toDataFormat', function( evt ) {
              evt.data.dataValue.filterChildren( that.htmlFilter, true );
          }, null, null, 10 );
          
          // Transform outcoming "data".
          // Add element filter after htmlDataProcessor.htmlFilter when preparing output data HTML.
          editor.on( 'toDataFormat', function( evt ) {
              evt.data.filter.applyTo( evt.data.dataValue, false, true );
          }, null, null, 11 );
          
          editor.on( 'toDataFormat', function( evt ) {
              var data = evt.data.dataValue,
                  writer = that.writer;
          
              writer.reset();
              data.writeChildrenHtml( writer );
              data = writer.getHtml( true );
          
              // Restore those non-HTML protected source. (https://dev.ckeditor.com/ticket/4475,https://dev.ckeditor.com/ticket/4880)
              data = unprotectRealComments( data );
              data = unprotectSource( data, editor );
          
              evt.data.dataValue = data;
          }, null, null, 15 );

          總結

          編輯器內容的設置和獲取表面上是簡單只是調用一個方法就完成了,但是其實內部的流程還是很長的,大致分為:

          1. 消息告知saveSnapshot
          2. 準備工作setData
          3. 處理流程dataProcessor
          4. 發送事件 toHtml
          5. 系統事件(優先級小于10)處理 protectfilter
          6. 系統事件(優先級大于10)處理,進行最后的兜底(插入或獲取)邏輯 CKEDITOR.htmlParser.basicWriter

          擊右上方,關注開源中國OSC頭條號,獲取最新技術資訊

          CKEditor 5 v11.2.0 發布了,CKEditor 是一個網頁在線文字編輯器,特點是高性能與可擴展。

          此版本帶來了期待已久的 Office 粘貼支持,例如可以直接復制 Microsoft Word 的文檔,還集成了 CKFinder 文件上傳器,此外,完善了圖像上傳文檔,改進了移動設備上的 UI,并引入了許多其它功能和改進。

          復制 Word 文檔

          此新功能允許粘貼 Microsoft Word 中的內容并保留原始結構和格式,包括基本文本樣式、標題級別、鏈接、列表、表格和圖像等。

          集成 CKFinder

          CKFinder 是一個文件管理器和上傳器,它是 CKEditor 4 的一個關鍵特性,現在已經集成到了此版本的 CKEditor 5 中。集成之后允許將圖像以及文件鏈接插入到編輯內容中。

          其它變更

          插件開發者需要關注一下,Position、Range 和 Writer 引擎類 API 已經發生變化。在此版本中,對 @ckeditor/ckeditor5-engine 軟件包中的公共 API 進行了一些重大更改,view 和 model 中不再開放 Position 和 Range 類的各種方法,例如Position#createAt()、Position#createFromParentAndOffset()、Range#createIn() 和Range#createCollapsedAt()。它們已被相應的方法替換,例如 createPositionAt( parent, offset )、createRangeIn() 和createRange( position )。

          比如之前的寫法:

          import Position from '@ckeditor/ckeditor5-engine/src/model/position';
          import Range from '@ckeditor/ckeditor5-engine/src/model/range';
          // ...
          editor.model.change( writer => {
           const imageElement = writer.createElement( 'image', {
           src: 'https://example.com/image.jpg'
           } );
           // Insert the image at the current selection location.
           editor.model.insertContent( imageElement, editor.model.document.selection );
           const paragraph = writer.createElement( 'paragraph' );
           const insertPosition = Position.createAfter( imageElement );
           writer.insert( paragraph, insertPosition );
           // Set the selection in the <paragraph>.
           writer.setSelection( Range.createIn( paragraph ) );
          } );
          

          現在這樣寫:

          // Imports from `@ckeditor/ckeditor5-engine` are no longer needed.
          // ...
          editor.model.change( writer => {
           const imageElement = writer.createElement( 'image', {
           src: 'https://example.com/image.jpg'
           } );
           editor.model.insertContent( imageElement );
           const paragraph = writer.createElement( 'paragraph' );
           // Writer#createPositionAfter() instead of Position#createAfter().
           const insertPosition = writer.createPositionAfter( imageElement );
           writer.insert( paragraph, insertPosition );
           // Writer#createRangeIn() instead of Range#createIn().
           writer.setSelection( writer.createRangeIn( paragraph ) );
          } );
          

          移動設備上更好的 UI

          改進的 UI 帶有下拉面板定位,可確保下拉內容始終對用戶可見。還使一些 UI 元素根據環境響應,例如,鏈接和圖像文本替代小氣球圖標現在適應屏幕的大小,使觸摸按鈕更容易。

          其它更新內容,點擊下方“了解更多”,查看發布公告。

          ↓↓↓

          KEditor 5 v23.1.0 穩定版已發布,主要更新內容包括:支持在編輯器嵌入原生 HTML 代碼并進行渲染、改進 reconversion API 以及支持將表格內容粘貼到另一個表格。

          嵌入 Raw HTML

          此功能支持在編輯器中嵌入任意 HTML 代碼片段,因此用戶可以嵌入其他 CKEditor 5 功能不支持的 HTML 代碼,并繞過編輯器的過濾機制。

          因此該功能具有一定的風險,因為錯誤的配置可能會導致安全問題。在啟用 HTML 嵌入內容預覽功能前,請始終使用 sanitizer ,并使用 CSP 額外保護網站。

          詳細用法查看使用文檔。

          將表格內容粘貼到另一個表格

          支持多種情況:

          • 將表格粘貼到選定的多單元表格片段中
          • 將較小的表格片段粘貼到較大的選定表格片段中
          • 當僅選擇一個單元格時只粘貼一個表單項

          其他改進和 bugfix

          • 使用新指南和 Demo
          • 集成 React
          • 不支持的元素會導致 JavaScript 錯誤,而不是被過濾掉
          • 打開上傳面板時將 URL 輸入作為焦點
          • ……

          主站蜘蛛池模板: 国产av一区二区三区日韩| 日韩高清国产一区在线| 精品91一区二区三区| 久久影院亚洲一区| 精品国产福利第一区二区三区| 亚洲日韩激情无码一区| 亚洲国产精品一区二区三区久久 | 国产精品视频分类一区| 亚洲第一区香蕉_国产a| 成人无码AV一区二区| 亚洲中文字幕无码一区二区三区| 国产伦精品一区二区三区在线观看 | 夜夜添无码试看一区二区三区| 亚洲中文字幕丝袜制服一区| 少妇激情AV一区二区三区| 国产一区二区三区免费观在线| 爆乳熟妇一区二区三区| 日本高清不卡一区| 日本无卡码一区二区三区| 波多野结衣一区二区免费视频| 精品国产日韩一区三区| 色欲AV无码一区二区三区| 国产日韩精品一区二区三区在线| 国产色综合一区二区三区| 亚洲AV无码国产精品永久一区 | 国产成人精品第一区二区| 亚洲一区二区观看播放| 日本免费精品一区二区三区| 香蕉视频一区二区三区| 亚洲中文字幕在线无码一区二区| 国产精品无码一区二区在线| 一区二区三区免费精品视频| 久久国产精品无码一区二区三区 | 国产一区二区三区在线观看影院| 日韩少妇无码一区二区三区| 日韩精品成人一区二区三区| 精品国产一区二区三区| 精品一区二区三区四区在线播放| 青娱乐国产官网极品一区 | 精品国产免费一区二区三区香蕉| 97人妻无码一区二区精品免费|