]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/core/htmlparser/element.js
224d3e6a16dab29704abb717e717f6775f0e1c19
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
9 * A lightweight representation of an HTML element.
12 * @extends CKEDITOR.htmlParser.node
13 * @constructor Creates an element class instance.
14 * @param {String} name The element name.
15 * @param {Object} attributes An object storing all attributes defined for
18 CKEDITOR
. htmlParser
. element = function ( name
, attributes
) {
27 * Stores the attributes defined for this element.
31 this . attributes
= attributes
|| {};
34 * The nodes that are direct children of this element.
38 // Reveal the real semantic of our internal custom tag name (http://dev.ckeditor.com/ticket/6639),
39 // when resolving whether it's block like.
40 var realName
= name
|| '' ,
41 prefixed
= realName
. match ( /^cke:(.*)/ );
42 prefixed
&& ( realName
= prefixed
[ 1 ] );
44 var isBlockLike
= !!( CKEDITOR
. dtd
.$ nonBodyContent
[ realName
] || CKEDITOR
. dtd
.$ block
[ realName
] ||
45 CKEDITOR
. dtd
.$ listItem
[ realName
] || CKEDITOR
. dtd
.$ tableContent
[ realName
] ||
46 CKEDITOR
. dtd
.$ nonEditable
[ realName
] || realName
== 'br' );
48 this . isEmpty
= !! CKEDITOR
. dtd
.$ empty
[ name
];
49 this . isUnknown
= ! CKEDITOR
. dtd
[ name
];
53 isBlockLike : isBlockLike
,
54 hasInlineStarted : this . isEmpty
|| ! isBlockLike
59 * Object presentation of the CSS style declaration text.
62 * @constructor Creates a `cssStyle` class instance.
63 * @param {CKEDITOR.htmlParser.element/String} elementOrStyleText
64 * An HTML parser element or the inline style text.
66 CKEDITOR
. htmlParser
. cssStyle = function () {
71 styleText
= arg
instanceof CKEDITOR
. htmlParser
. element
? arg
. attributes
. style : arg
;
73 // html-encoded quote might be introduced by 'font-family'
74 // from MS-Word which confused the following regexp. e.g.
75 //'font-family: "Lucida, Console"'
76 // TODO reuse CSS methods from tools.
77 ( styleText
|| '' ). replace ( /"/g , '"' ). replace ( /\ s
*([^ :;]+)\ s
*:\ s
*([^;]+)\ s
*(?=;|$)/ g
, function ( match
, name
, value
) {
78 name
== 'font-family' && ( value
= value
. replace ( /["']/g , '' ) );
79 rules
[ name
. toLowerCase () ] = value
;
87 * Applies the styles to the specified element or object.
89 * @param {CKEDITOR.htmlParser.element/CKEDITOR.dom.element/Object} obj
91 populate : function ( obj
) {
92 var style
= this . toString ();
94 obj
instanceof CKEDITOR
. dom
. element
? obj
. setAttribute ( 'style' , style
) : obj
instanceof CKEDITOR
. htmlParser
. element
? obj
. attributes
. style
= style : obj
. style
= style
;
99 * Serializes CSS style declaration to a string.
103 toString : function () {
105 for ( var i
in rules
)
106 rules
[ i
] && output
. push ( i
, ':' , rules
[ i
], ';' );
107 return output
. join ( '' );
112 /** @class CKEDITOR.htmlParser.element */
114 // Used to sort attribute entries in an array, where the first element of
115 // each object is the attribute name.
116 var sortAttribs = function ( a
, b
) {
119 return a
< b
? - 1 : a
> b
? 1 : 0 ;
121 fragProto
= CKEDITOR
. htmlParser
. fragment
. prototype ;
123 CKEDITOR
. htmlParser
. element
. prototype = CKEDITOR
. tools
. extend ( new CKEDITOR
. htmlParser
. node (), {
125 * The node type. This is a constant value set to {@link CKEDITOR#NODE_ELEMENT}.
128 * @property {Number} [=CKEDITOR.NODE_ELEMENT]
130 type : CKEDITOR
. NODE_ELEMENT
,
133 * Adds a node to the element children list.
136 * @param {CKEDITOR.htmlParser.node} node The node to be added.
137 * @param {Number} [index] From where the insertion happens.
142 * Clones this element.
144 * @returns {CKEDITOR.htmlParser.element} The element clone.
147 return new CKEDITOR
. htmlParser
. element ( this . name
, this . attributes
);
151 * Filters this element and its children with the given filter.
154 * @param {CKEDITOR.htmlParser.filter} filter
155 * @returns {Boolean} The method returns `false` when this element has
156 * been removed or replaced with another. This information means that
157 * {@link #filterChildren} has to repeat the filter on the current
158 * position in parent's children array.
160 filter : function ( filter
, context
) {
164 context
= element
. getFilterContext ( context
);
166 // Do not process elements with data-cke-processor attribute set to off.
170 // Filtering if it's the root node.
171 if ( ! element
. parent
)
172 filter
. onRoot ( context
, element
);
175 originalName
= element
. name
;
177 if ( !( name
= filter
. onElementName ( context
, originalName
) ) ) {
184 if ( !( element
= filter
. onElement ( context
, element
) ) ) {
189 // New element has been returned - replace current one
190 // and process it (stop processing this and return false, what
191 // means that element has been removed).
192 if ( element
!== this ) {
193 this . replaceWith ( element
);
197 // If name has been changed - continue loop, so in next iteration
198 // filters for new name will be applied to this element.
199 // If name hasn't been changed - stop.
200 if ( element
. name
== originalName
)
203 // If element has been replaced with something of a
204 // different type, then make the replacement filter itself.
205 if ( element
. type
!= CKEDITOR
. NODE_ELEMENT
) {
206 this . replaceWith ( element
);
210 // This indicate that the element has been dropped by
211 // filter but not the children.
212 if ( ! element
. name
) {
213 this . replaceWithChildren ();
218 var attributes
= element
. attributes
,
219 a
, value
, newAttrName
;
221 for ( a
in attributes
) {
223 value
= attributes
[ a
];
225 // Loop until name isn't modified.
226 // A little bit senseless, but IE would do that anyway
227 // because it iterates with for-in loop even over properties
228 // created during its run.
230 if ( !( newAttrName
= filter
. onAttributeName ( context
, a
) ) ) {
231 delete attributes
[ a
];
233 } else if ( newAttrName
!= a
) {
234 delete attributes
[ a
];
243 if ( ( value
= filter
. onAttribute ( context
, element
, newAttrName
, value
) ) === false )
244 delete attributes
[ newAttrName
];
246 attributes
[ newAttrName
] = value
;
250 if ( ! element
. isEmpty
)
251 this . filterChildren ( filter
, false , context
);
257 * Filters this element's children with the given filter.
259 * Element's children may only be filtered once by one
260 * instance of the filter.
262 * @method filterChildren
263 * @param {CKEDITOR.htmlParser.filter} filter
265 filterChildren : fragProto
. filterChildren
,
268 * Writes the element HTML to the CKEDITOR.htmlWriter.
270 * @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which HTML will be written.
271 * @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
272 * **Note:** It is unsafe to filter an offline (not appended) node.
274 writeHtml : function ( writer
, filter
) {
276 this . filter ( filter
);
278 var name
= this . name
,
280 attributes
= this . attributes
,
285 writer
. openTag ( name
, attributes
);
287 // Copy all attributes to an array.
288 for ( attrName
in attributes
)
289 attribsArray
. push ( [ attrName
, attributes
[ attrName
] ] );
291 // Sort the attributes by name.
292 if ( writer
. sortAttributes
)
293 attribsArray
. sort ( sortAttribs
);
295 // Send the attributes.
296 for ( i
= 0 , l
= attribsArray
. length
; i
< l
; i
++ ) {
297 attr
= attribsArray
[ i
];
298 writer
. attribute ( attr
[ 0 ], attr
[ 1 ] );
302 writer
. openTagClose ( name
, this . isEmpty
);
304 this . writeChildrenHtml ( writer
);
306 // Close the element.
308 writer
. closeTag ( name
);
312 * Sends children of this element to the writer.
314 * @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which HTML will be written.
315 * @param {CKEDITOR.htmlParser.filter} [filter]
317 writeChildrenHtml : fragProto
. writeChildrenHtml
,
320 * Replaces this element with its children.
324 replaceWithChildren : function () {
325 var children
= this . children
;
327 for ( var i
= children
. length
; i
; )
328 children
[ -- i
]. insertAfter ( this );
334 * Executes a callback on each node (of the given type) in this element.
336 * // Create a <p> element with foo<b>bar</b>bom as its content.
337 * var elP = CKEDITOR.htmlParser.fragment.fromHtml( 'foo<b>bar</b>bom', 'p' );
338 * elP.forEach( function( node ) {
339 * console.log( node );
342 * // 1. document fragment,
344 * // 3. "foo" text node,
346 * // 5. "bar" text node,
347 * // 6. "bom" text node.
350 * @param {Function} callback Function to be executed on every node.
351 * **Since 4.3**: If `callback` returned `false`, the descendants of the current node will be ignored.
352 * @param {CKEDITOR.htmlParser.node} callback.node Node passed as an argument.
353 * @param {Number} [type] Whether the specified `callback` will be executed only on nodes of this type.
354 * @param {Boolean} [skipRoot] Do not execute `callback` on this element.
356 forEach : fragProto
. forEach
,
359 * Gets this element's first child. If `condition` is given, this method returns
360 * the first child which satisfies that condition.
363 * @param {String/Object/Function} condition Name of a child, a hash of names, or a validator function.
364 * @returns {CKEDITOR.htmlParser.node}
366 getFirst : function ( condition
) {
368 return this . children
. length
? this . children
[ 0 ] : null ;
370 if ( typeof condition
!= 'function' )
371 condition
= nameCondition ( condition
);
373 for ( var i
= 0 , l
= this . children
. length
; i
< l
; ++ i
) {
374 if ( condition ( this . children
[ i
] ) )
375 return this . children
[ i
];
381 * Gets this element's inner HTML.
386 getHtml : function () {
387 var writer
= new CKEDITOR
. htmlParser
. basicWriter ();
388 this . writeChildrenHtml ( writer
);
389 return writer
. getHtml ();
393 * Sets this element's inner HTML.
396 * @param {String} html
398 setHtml : function ( html
) {
399 var children
= this . children
= CKEDITOR
. htmlParser
. fragment
. fromHtml ( html
). children
;
401 for ( var i
= 0 , l
= children
. length
; i
< l
; ++ i
)
402 children
[ i
]. parent
= this ;
406 * Gets this element's outer HTML.
411 getOuterHtml : function () {
412 var writer
= new CKEDITOR
. htmlParser
. basicWriter ();
413 this . writeHtml ( writer
);
414 return writer
. getHtml ();
418 * Splits this element at the given index.
421 * @param {Number} index Index at which the element will be split — `0` means the beginning,
422 * `1` after the first child node, etc.
423 * @returns {CKEDITOR.htmlParser.element} The new element following this one.
425 split : function ( index
) {
426 var cloneChildren
= this . children
. splice ( index
, this . children
. length
- index
),
427 clone
= this . clone ();
429 for ( var i
= 0 ; i
< cloneChildren
. length
; ++ i
)
430 cloneChildren
[ i
]. parent
= clone
;
432 clone
. children
= cloneChildren
;
434 if ( cloneChildren
[ 0 ] )
435 cloneChildren
[ 0 ]. previous
= null ;
438 this . children
[ index
- 1 ]. next
= null ;
440 this . parent
. add ( clone
, this . getIndex () + 1 );
446 * Searches through the current node children to find nodes matching the `criteria`.
448 * @param {String/Function} criteria Tag name or evaluator function.
449 * @param {Boolean} [recursive=false]
450 * @returns {CKEDITOR.htmlParser.node[]}
452 find : function ( criteria
, recursive
) {
453 if ( recursive
=== undefined ) {
460 for ( i
= 0 ; i
< this . children
. length
; i
++ ) {
461 var curChild
= this . children
[ i
];
463 if ( typeof criteria
== 'function' && criteria ( curChild
) ) {
464 ret
. push ( curChild
);
465 } else if ( typeof criteria
== 'string' && curChild
. name
=== criteria
) {
466 ret
. push ( curChild
);
469 if ( recursive
&& curChild
. find
) {
470 ret
= ret
. concat ( curChild
. find ( criteria
, recursive
) );
478 * Adds a class name to the list of classes.
481 * @param {String} className The class name to be added.
483 addClass : function ( className
) {
484 if ( this . hasClass ( className
) )
487 var c
= this . attributes
[ 'class' ] || '' ;
489 this . attributes
[ 'class' ] = c
+ ( c
? ' ' : '' ) + className
;
493 * Removes a class name from the list of classes.
496 * @param {String} className The class name to be removed.
498 removeClass : function ( className
) {
499 var classes
= this . attributes
[ 'class' ];
504 // We can safely assume that className won't break regexp.
505 // http://stackoverflow.com/questions/448981/what-characters-are-valid-in-css-class-names
506 classes
= CKEDITOR
. tools
. trim ( classes
. replace ( new RegExp ( '(?: \\ s+|^)' + className
+ '(?: \\ s+|$)' ), ' ' ) );
509 this . attributes
[ 'class' ] = classes
;
511 delete this . attributes
[ 'class' ];
515 * Checkes whether this element has a class name.
518 * @param {String} className The class name to be checked.
519 * @returns {Boolean} Whether this element has a `className`.
521 hasClass : function ( className
) {
522 var classes
= this . attributes
[ 'class' ];
527 return ( new RegExp ( '(?:^| \\ s)' + className
+ '(?= \\ s|$)' ) ). test ( classes
);
530 getFilterContext : function ( ctx
) {
537 nestedEditable : false
541 if ( ! ctx
. off
&& this . attributes
[ 'data-cke-processor' ] == 'off' )
542 changes
. push ( 'off' , true );
544 if ( ! ctx
. nonEditable
&& this . attributes
. contenteditable
== 'false' )
545 changes
. push ( 'nonEditable' , true );
546 // A context to be given nestedEditable must be nonEditable first (by inheritance) (http://dev.ckeditor.com/ticket/11372, http://dev.ckeditor.com/ticket/11698).
547 // Special case: http://dev.ckeditor.com/ticket/11504 - filter starts on <body contenteditable=true>,
548 // so ctx.nonEditable has not been yet set to true.
549 else if ( ctx
. nonEditable
&& ! ctx
. nestedEditable
&& this . attributes
. contenteditable
== 'true' )
550 changes
. push ( 'nestedEditable' , true );
552 if ( changes
. length
) {
553 ctx
= CKEDITOR
. tools
. copy ( ctx
);
554 for ( var i
= 0 ; i
< changes
. length
; i
+= 2 )
555 ctx
[ changes
[ i
] ] = changes
[ i
+ 1 ];
562 function nameCondition ( condition
) {
563 return function ( el
) {
564 return el
. type
== CKEDITOR
. NODE_ELEMENT
&&
565 ( typeof condition
== 'string' ? el
. name
== condition : el
. name
in condition
);