plugin.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /**
  2. * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. ( function() {
  6. var cssStyle = CKEDITOR.htmlParser.cssStyle,
  7. cssLength = CKEDITOR.tools.cssLength;
  8. var cssLengthRegex = /^((?:\d*(?:\.\d+))|(?:\d+))(.*)?$/i;
  9. // Replacing the former CSS length value with the later one, with
  10. // adjustment to the length unit.
  11. function replaceCssLength( length1, length2 ) {
  12. var parts1 = cssLengthRegex.exec( length1 ),
  13. parts2 = cssLengthRegex.exec( length2 );
  14. // Omit pixel length unit when necessary,
  15. // e.g. replaceCssLength( 10, '20px' ) -> 20
  16. if ( parts1 ) {
  17. if ( !parts1[ 2 ] && parts2[ 2 ] == 'px' )
  18. return parts2[ 1 ];
  19. if ( parts1[ 2 ] == 'px' && !parts2[ 2 ] )
  20. return parts2[ 1 ] + 'px';
  21. }
  22. return length2;
  23. }
  24. CKEDITOR.plugins.add( 'fakeobjects', {
  25. // jscs:disable maximumLineLength
  26. lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
  27. // jscs:enable maximumLineLength
  28. init: function( editor ) {
  29. // Allow image with all styles and classes plus src, alt and title attributes.
  30. // We need them when fakeobject is pasted.
  31. editor.filter.allow( 'img[!data-cke-realelement,src,alt,title](*){*}', 'fakeobjects' );
  32. },
  33. afterInit: function( editor ) {
  34. var dataProcessor = editor.dataProcessor,
  35. htmlFilter = dataProcessor && dataProcessor.htmlFilter;
  36. if ( htmlFilter ) {
  37. htmlFilter.addRules( createHtmlFilterRules( editor ), {
  38. applyToAll: true
  39. } );
  40. }
  41. }
  42. } );
  43. /**
  44. * Creates fake {@link CKEDITOR.dom.element} based on real element.
  45. * Fake element is an img with special attributes, which keep real element properties.
  46. *
  47. * @member CKEDITOR.editor
  48. * @param {CKEDITOR.dom.element} realElement Real element to transform.
  49. * @param {String} className Class name which will be used as class of fake element.
  50. * @param {String} realElementType Stores type of fake element.
  51. * @param {Boolean} isResizable Keeps information if element is resizable.
  52. * @returns {CKEDITOR.dom.element} Fake element.
  53. */
  54. CKEDITOR.editor.prototype.createFakeElement = function( realElement, className, realElementType, isResizable ) {
  55. var lang = this.lang.fakeobjects,
  56. label = lang[ realElementType ] || lang.unknown;
  57. var attributes = {
  58. 'class': className,
  59. 'data-cke-realelement': encodeURIComponent( realElement.getOuterHtml() ),
  60. 'data-cke-real-node-type': realElement.type,
  61. alt: label,
  62. title: label,
  63. align: realElement.getAttribute( 'align' ) || ''
  64. };
  65. // Do not set "src" on high-contrast so the alt text is displayed. (https://dev.ckeditor.com/ticket/8945)
  66. if ( !CKEDITOR.env.hc )
  67. attributes.src = CKEDITOR.tools.transparentImageData;
  68. if ( realElementType )
  69. attributes[ 'data-cke-real-element-type' ] = realElementType;
  70. if ( isResizable ) {
  71. attributes[ 'data-cke-resizable' ] = isResizable;
  72. var fakeStyle = new cssStyle();
  73. var width = realElement.getAttribute( 'width' ),
  74. height = realElement.getAttribute( 'height' );
  75. width && ( fakeStyle.rules.width = cssLength( width ) );
  76. height && ( fakeStyle.rules.height = cssLength( height ) );
  77. fakeStyle.populate( attributes );
  78. }
  79. return this.document.createElement( 'img', { attributes: attributes } );
  80. };
  81. /**
  82. * Creates fake {@link CKEDITOR.htmlParser.element} based on real element.
  83. *
  84. * @member CKEDITOR.editor
  85. * @param {CKEDITOR.dom.element} realElement Real element to transform.
  86. * @param {String} className Class name which will be used as class of fake element.
  87. * @param {String} realElementType Store type of fake element.
  88. * @param {Boolean} isResizable Keep information if element is resizable.
  89. * @returns {CKEDITOR.htmlParser.element} Fake htmlParser element.
  90. */
  91. CKEDITOR.editor.prototype.createFakeParserElement = function( realElement, className, realElementType, isResizable ) {
  92. var lang = this.lang.fakeobjects,
  93. label = lang[ realElementType ] || lang.unknown,
  94. html;
  95. var writer = new CKEDITOR.htmlParser.basicWriter();
  96. realElement.writeHtml( writer );
  97. html = writer.getHtml();
  98. var attributes = {
  99. 'class': className,
  100. 'data-cke-realelement': encodeURIComponent( html ),
  101. 'data-cke-real-node-type': realElement.type,
  102. alt: label,
  103. title: label,
  104. align: realElement.attributes.align || ''
  105. };
  106. // Do not set "src" on high-contrast so the alt text is displayed. (https://dev.ckeditor.com/ticket/8945)
  107. if ( !CKEDITOR.env.hc )
  108. attributes.src = CKEDITOR.tools.transparentImageData;
  109. if ( realElementType )
  110. attributes[ 'data-cke-real-element-type' ] = realElementType;
  111. if ( isResizable ) {
  112. attributes[ 'data-cke-resizable' ] = isResizable;
  113. var realAttrs = realElement.attributes,
  114. fakeStyle = new cssStyle();
  115. var width = realAttrs.width,
  116. height = realAttrs.height;
  117. width !== undefined && ( fakeStyle.rules.width = cssLength( width ) );
  118. height !== undefined && ( fakeStyle.rules.height = cssLength( height ) );
  119. fakeStyle.populate( attributes );
  120. }
  121. return new CKEDITOR.htmlParser.element( 'img', attributes );
  122. };
  123. /**
  124. * Creates {@link CKEDITOR.dom.element} from fake element.
  125. *
  126. * @member CKEDITOR.editor
  127. * @param {CKEDITOR.dom.element} fakeElement Fake element to transform.
  128. * @returns {CKEDITOR.dom.element/null} Returns real element or `null` if transformed element wasn't fake.
  129. */
  130. CKEDITOR.editor.prototype.restoreRealElement = function( fakeElement ) {
  131. if ( fakeElement.data( 'cke-real-node-type' ) != CKEDITOR.NODE_ELEMENT )
  132. return null;
  133. var realElementHtml = decodeURIComponent( fakeElement.data( 'cke-realelement' ) ),
  134. filteredHtml = filterHtml( this, realElementHtml ),
  135. realElement = CKEDITOR.dom.element.createFromHtml( filteredHtml, this.document );
  136. if ( fakeElement.data( 'cke-resizable' ) ) {
  137. var width = fakeElement.getStyle( 'width' ),
  138. height = fakeElement.getStyle( 'height' );
  139. width && realElement.setAttribute( 'width', replaceCssLength( realElement.getAttribute( 'width' ), width ) );
  140. height && realElement.setAttribute( 'height', replaceCssLength( realElement.getAttribute( 'height' ), height ) );
  141. }
  142. return realElement;
  143. };
  144. function createHtmlFilterRules( editor ) {
  145. return {
  146. elements: {
  147. $: function( element ) {
  148. var attributes = element.attributes,
  149. realHtml = attributes && attributes[ 'data-cke-realelement' ],
  150. filteredRealHtml = filterHtml( editor, decodeURIComponent( realHtml ) ),
  151. realFragment = realHtml && new CKEDITOR.htmlParser.fragment.fromHtml( filteredRealHtml ),
  152. realElement = realFragment && realFragment.children[ 0 ];
  153. // Width/height in the fake object are subjected to clone into the real element.
  154. if ( realElement && element.attributes[ 'data-cke-resizable' ] ) {
  155. var styles = new cssStyle( element ).rules,
  156. realAttrs = realElement.attributes,
  157. width = styles.width,
  158. height = styles.height;
  159. width && ( realAttrs.width = replaceCssLength( realAttrs.width, width ) );
  160. height && ( realAttrs.height = replaceCssLength( realAttrs.height, height ) );
  161. }
  162. return realElement;
  163. }
  164. }
  165. };
  166. }
  167. // Content stored inside fake element is raw and should be explicitly
  168. // passed to ACF filter. Additionally some elements can have prefixes in tag names,
  169. // which should be removed before filtering and added after it.
  170. function filterHtml( editor, html ) {
  171. var unprefixedElements = [],
  172. prefixRegex = /^cke:/i,
  173. dataFilter = new CKEDITOR.htmlParser.filter( {
  174. elements: {
  175. '^': function( element ) {
  176. if ( prefixRegex.test( element.name ) ) {
  177. element.name = element.name.replace( prefixRegex, '' );
  178. unprefixedElements.push( element );
  179. }
  180. },
  181. iframe: function( element ) {
  182. element.children = [];
  183. }
  184. }
  185. } ),
  186. acfFilter = editor.activeFilter,
  187. writer = new CKEDITOR.htmlParser.basicWriter(),
  188. fragment = CKEDITOR.htmlParser.fragment.fromHtml( html );
  189. dataFilter.applyTo( fragment );
  190. acfFilter.applyTo( fragment );
  191. CKEDITOR.tools.array.forEach( unprefixedElements, function( element ) {
  192. element.name = 'cke:' + element.name;
  193. } );
  194. fragment.writeHtml( writer );
  195. return writer.getHtml();
  196. }
  197. } )();