aboutsummaryrefslogtreecommitdiff
path: root/sources/core/dom/element.js
diff options
context:
space:
mode:
Diffstat (limited to 'sources/core/dom/element.js')
-rw-r--r--sources/core/dom/element.js130
1 files changed, 103 insertions, 27 deletions
diff --git a/sources/core/dom/element.js b/sources/core/dom/element.js
index b586b02..31451f9 100644
--- a/sources/core/dom/element.js
+++ b/sources/core/dom/element.js
@@ -1,5 +1,5 @@
1/** 1/**
2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. 2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license 3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */ 4 */
5 5
@@ -308,7 +308,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
308 */ 308 */
309 appendText: function( text ) { 309 appendText: function( text ) {
310 // On IE8 it is impossible to append node to script tag, so we use its text. 310 // On IE8 it is impossible to append node to script tag, so we use its text.
311 // On the contrary, on Safari the text property is unpredictable in links. (#13232) 311 // On the contrary, on Safari the text property is unpredictable in links. (http://dev.ckeditor.com/ticket/13232)
312 if ( this.$.text != null && CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) 312 if ( this.$.text != null && CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
313 this.$.text += text; 313 this.$.text += text;
314 else 314 else
@@ -368,13 +368,36 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
368 range.setEndAfter( parent ); 368 range.setEndAfter( parent );
369 369
370 // Extract it. 370 // Extract it.
371 var docFrag = range.extractContents( false, cloneId || false ); 371 var docFrag = range.extractContents( false, cloneId || false ),
372 tmpElement,
373 current;
372 374
373 // Move the element outside the broken element. 375 // Move the element outside the broken element.
374 range.insertNode( this.remove() ); 376 range.insertNode( this.remove() );
375 377
376 // Re-insert the extracted piece after the element. 378 // In case of Internet Explorer, we must check if there is no background-color
377 docFrag.insertAfterNode( this ); 379 // added to the element. In such case, we have to overwrite it to prevent "switching it off"
380 // by a browser (http://dev.ckeditor.com/ticket/14667).
381 if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
382 tmpElement = new CKEDITOR.dom.element( 'div' );
383
384 while ( current = docFrag.getFirst() ) {
385 if ( current.$.style.backgroundColor ) {
386 // This is a necessary hack to make sure that IE will track backgroundColor CSS property, see
387 // http://dev.ckeditor.com/ticket/14667#comment:8 for more details.
388 current.$.style.backgroundColor = current.$.style.backgroundColor;
389 }
390
391 tmpElement.append( current );
392 }
393
394 // Re-insert the extracted piece after the element.
395 tmpElement.insertAfter( this );
396 tmpElement.remove( true );
397 } else {
398 // Re-insert the extracted piece after the element.
399 docFrag.insertAfterNode( this );
400 }
378 }, 401 },
379 402
380 /** 403 /**
@@ -429,7 +452,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
429 */ 452 */
430 getHtml: function() { 453 getHtml: function() {
431 var retval = this.$.innerHTML; 454 var retval = this.$.innerHTML;
432 // Strip <?xml:namespace> tags in IE. (#3341). 455 // Strip <?xml:namespace> tags in IE. (http://dev.ckeditor.com/ticket/3341).
433 return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval; 456 return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval;
434 }, 457 },
435 458
@@ -444,7 +467,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
444 getOuterHtml: function() { 467 getOuterHtml: function() {
445 if ( this.$.outerHTML ) { 468 if ( this.$.outerHTML ) {
446 // IE includes the <?xml:namespace> tag in the outerHTML of 469 // IE includes the <?xml:namespace> tag in the outerHTML of
447 // namespaced element. So, we must strip it here. (#3341) 470 // namespaced element. So, we must strip it here. (http://dev.ckeditor.com/ticket/3341)
448 return this.$.outerHTML.replace( /<\?[^>]*>/, '' ); 471 return this.$.outerHTML.replace( /<\?[^>]*>/, '' );
449 } 472 }
450 473
@@ -595,7 +618,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
595 return this.$[ name ]; 618 return this.$[ name ];
596 619
597 case 'style': 620 case 'style':
598 // IE does not return inline styles via getAttribute(). See #2947. 621 // IE does not return inline styles via getAttribute(). See http://dev.ckeditor.com/ticket/2947.
599 return this.$.style.cssText; 622 return this.$.style.cssText;
600 623
601 case 'contenteditable': 624 case 'contenteditable':
@@ -611,6 +634,28 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
611 } )(), 634 } )(),
612 635
613 /** 636 /**
637 * Gets the values of all element attributes.
638 *
639 * @param {Array} exclude The names of attributes to be excluded from the returned object.
640 * @return {Object} An object containing all element attributes with their values.
641 */
642 getAttributes: function( exclude ) {
643 var attributes = {},
644 attrDefs = this.$.attributes,
645 i;
646
647 exclude = CKEDITOR.tools.isArray( exclude ) ? exclude : [];
648
649 for ( i = 0; i < attrDefs.length; i++ ) {
650 if ( CKEDITOR.tools.indexOf( exclude, attrDefs[ i ].name ) === -1 ) {
651 attributes[ attrDefs[ i ].name ] = attrDefs[ i ].value;
652 }
653 }
654
655 return attributes;
656 },
657
658 /**
614 * Gets the nodes list containing all children of this element. 659 * Gets the nodes list containing all children of this element.
615 * 660 *
616 * @returns {CKEDITOR.dom.nodeList} 661 * @returns {CKEDITOR.dom.nodeList}
@@ -634,7 +679,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
634 function( propertyName ) { 679 function( propertyName ) {
635 var style = this.getWindow().$.getComputedStyle( this.$, null ); 680 var style = this.getWindow().$.getComputedStyle( this.$, null );
636 681
637 // Firefox may return null if we call the above on a hidden iframe. (#9117) 682 // Firefox may return null if we call the above on a hidden iframe. (http://dev.ckeditor.com/ticket/9117)
638 return style ? style.getPropertyValue( propertyName ) : ''; 683 return style ? style.getPropertyValue( propertyName ) : '';
639 } : function( propertyName ) { 684 } : function( propertyName ) {
640 return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ]; 685 return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ];
@@ -927,7 +972,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
927 elementWindow, elementWindowFrame; 972 elementWindow, elementWindowFrame;
928 973
929 // Webkit and Opera report non-zero offsetHeight despite that 974 // Webkit and Opera report non-zero offsetHeight despite that
930 // element is inside an invisible iframe. (#4542) 975 // element is inside an invisible iframe. (http://dev.ckeditor.com/ticket/4542)
931 if ( isVisible && CKEDITOR.env.webkit ) { 976 if ( isVisible && CKEDITOR.env.webkit ) {
932 elementWindow = this.getWindow(); 977 elementWindow = this.getWindow();
933 978
@@ -988,7 +1033,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
988 // attribute, which will be marked as "specified", even if the 1033 // attribute, which will be marked as "specified", even if the
989 // outerHTML of the element is not displaying the class attribute. 1034 // outerHTML of the element is not displaying the class attribute.
990 // Note : I was not able to reproduce it outside the editor, 1035 // Note : I was not able to reproduce it outside the editor,
991 // but I've faced it while working on the TC of #1391. 1036 // but I've faced it while working on the TC of http://dev.ckeditor.com/ticket/1391.
992 if ( this.getAttribute( 'class' ) ) { 1037 if ( this.getAttribute( 'class' ) ) {
993 return true; 1038 return true;
994 } 1039 }
@@ -1012,7 +1057,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1012 var attrs = this.$.attributes, 1057 var attrs = this.$.attributes,
1013 attrsNum = attrs.length; 1058 attrsNum = attrs.length;
1014 1059
1015 // The _moz_dirty attribute might get into the element after pasting (#5455) 1060 // The _moz_dirty attribute might get into the element after pasting (http://dev.ckeditor.com/ticket/5455)
1016 var execludeAttrs = { 'data-cke-expando': 1, _moz_dirty: 1 }; 1061 var execludeAttrs = { 'data-cke-expando': 1, _moz_dirty: 1 };
1017 1062
1018 return attrsNum > 0 && ( attrsNum > 2 || !execludeAttrs[ attrs[ 0 ].nodeName ] || ( attrsNum == 2 && !execludeAttrs[ attrs[ 1 ].nodeName ] ) ); 1063 return attrsNum > 0 && ( attrsNum > 2 || !execludeAttrs[ attrs[ 0 ].nodeName ] || ( attrsNum == 2 && !execludeAttrs[ attrs[ 1 ].nodeName ] ) );
@@ -1119,7 +1164,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1119 function mergeElements( element, sibling, isNext ) { 1164 function mergeElements( element, sibling, isNext ) {
1120 if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) { 1165 if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) {
1121 // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>, 1166 // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>,
1122 // queuing them to be moved later. (#5567) 1167 // queuing them to be moved later. (http://dev.ckeditor.com/ticket/5567)
1123 var pendingNodes = []; 1168 var pendingNodes = [];
1124 1169
1125 while ( sibling.data( 'cke-bookmark' ) || sibling.isEmptyInlineRemoveable() ) { 1170 while ( sibling.data( 'cke-bookmark' ) || sibling.isEmptyInlineRemoveable() ) {
@@ -1149,7 +1194,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1149 } 1194 }
1150 1195
1151 return function( inlineOnly ) { 1196 return function( inlineOnly ) {
1152 // Merge empty links and anchors also. (#5567) 1197 // Merge empty links and anchors also. (http://dev.ckeditor.com/ticket/5567)
1153 if ( !( inlineOnly === false || CKEDITOR.dtd.$removeEmpty[ this.getName() ] || this.is( 'a' ) ) ) { 1198 if ( !( inlineOnly === false || CKEDITOR.dtd.$removeEmpty[ this.getName() ] || this.is( 'a' ) ) ) {
1154 return; 1199 return;
1155 } 1200 }
@@ -1208,7 +1253,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1208 }; 1253 };
1209 } else if ( CKEDITOR.env.ie8Compat && CKEDITOR.env.secure ) { 1254 } else if ( CKEDITOR.env.ie8Compat && CKEDITOR.env.secure ) {
1210 return function( name, value ) { 1255 return function( name, value ) {
1211 // IE8 throws error when setting src attribute to non-ssl value. (#7847) 1256 // IE8 throws error when setting src attribute to non-ssl value. (http://dev.ckeditor.com/ticket/7847)
1212 if ( name == 'src' && value.match( /^http:\/\// ) ) { 1257 if ( name == 'src' && value.match( /^http:\/\// ) ) {
1213 try { 1258 try {
1214 standard.apply( this, arguments ); 1259 standard.apply( this, arguments );
@@ -1292,11 +1337,15 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1292 */ 1337 */
1293 removeAttributes: function( attributes ) { 1338 removeAttributes: function( attributes ) {
1294 if ( CKEDITOR.tools.isArray( attributes ) ) { 1339 if ( CKEDITOR.tools.isArray( attributes ) ) {
1295 for ( var i = 0; i < attributes.length; i++ ) 1340 for ( var i = 0; i < attributes.length; i++ ) {
1296 this.removeAttribute( attributes[ i ] ); 1341 this.removeAttribute( attributes[ i ] );
1342 }
1297 } else { 1343 } else {
1298 for ( var attr in attributes ) 1344 attributes = attributes || this.getAttributes();
1345
1346 for ( var attr in attributes ) {
1299 attributes.hasOwnProperty( attr ) && this.removeAttribute( attr ); 1347 attributes.hasOwnProperty( attr ) && this.removeAttribute( attr );
1348 }
1300 } 1349 }
1301 }, 1350 },
1302 1351
@@ -1442,7 +1491,8 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1442 body = doc.getBody(), 1491 body = doc.getBody(),
1443 quirks = doc.$.compatMode == 'BackCompat'; 1492 quirks = doc.$.compatMode == 'BackCompat';
1444 1493
1445 if ( document.documentElement.getBoundingClientRect ) { 1494 if ( document.documentElement.getBoundingClientRect &&
1495 ( CKEDITOR.env.ie ? CKEDITOR.env.version !== 8 : true ) ) {
1446 var box = this.$.getBoundingClientRect(), 1496 var box = this.$.getBoundingClientRect(),
1447 $doc = doc.$, 1497 $doc = doc.$,
1448 $docElem = $doc.documentElement; 1498 $docElem = $doc.documentElement;
@@ -1451,7 +1501,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1451 clientLeft = $docElem.clientLeft || body.$.clientLeft || 0, 1501 clientLeft = $docElem.clientLeft || body.$.clientLeft || 0,
1452 needAdjustScrollAndBorders = true; 1502 needAdjustScrollAndBorders = true;
1453 1503
1454 // #3804: getBoundingClientRect() works differently on IE and non-IE 1504 // http://dev.ckeditor.com/ticket/3804: getBoundingClientRect() works differently on IE and non-IE
1455 // browsers, regarding scroll positions. 1505 // browsers, regarding scroll positions.
1456 // 1506 //
1457 // On IE, the top position of the <html> element is always 0, no matter 1507 // On IE, the top position of the <html> element is always 0, no matter
@@ -1466,12 +1516,12 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1466 needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem ); 1516 needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem );
1467 } 1517 }
1468 1518
1469 // #12747. 1519 // http://dev.ckeditor.com/ticket/12747.
1470 if ( needAdjustScrollAndBorders ) { 1520 if ( needAdjustScrollAndBorders ) {
1471 var scrollRelativeLeft, 1521 var scrollRelativeLeft,
1472 scrollRelativeTop; 1522 scrollRelativeTop;
1473 1523
1474 // See #12758 to know more about document.(documentElement|body).scroll(Left|Top) in Webkit. 1524 // See http://dev.ckeditor.com/ticket/12758 to know more about document.(documentElement|body).scroll(Left|Top) in Webkit.
1475 if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version >= 12 ) ) { 1525 if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version >= 12 ) ) {
1476 scrollRelativeLeft = body.$.scrollLeft || $docElem.scrollLeft; 1526 scrollRelativeLeft = body.$.scrollLeft || $docElem.scrollLeft;
1477 scrollRelativeTop = body.$.scrollTop || $docElem.scrollTop; 1527 scrollRelativeTop = body.$.scrollTop || $docElem.scrollTop;
@@ -1553,7 +1603,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1553 parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight; 1603 parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight;
1554 1604
1555 // Skip body element, which will report wrong clientHeight when containing 1605 // Skip body element, which will report wrong clientHeight when containing
1556 // floated content. (#9523) 1606 // floated content. (http://dev.ckeditor.com/ticket/9523)
1557 if ( overflowed && !parent.is( 'body' ) ) 1607 if ( overflowed && !parent.is( 'body' ) )
1558 this.scrollIntoParent( parent, alignToTop, 1 ); 1608 this.scrollIntoParent( parent, alignToTop, 1 );
1559 1609
@@ -1626,6 +1676,16 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1626 return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0; 1676 return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0;
1627 } 1677 }
1628 1678
1679 // [WebKit] Reset stored scrollTop value to not break scrollIntoView() method flow.
1680 // Scrolling breaks when range.select() is used right after element.scrollIntoView(). (http://dev.ckeditor.com/ticket/14659)
1681 if ( CKEDITOR.env.webkit ) {
1682 var editor = this.getEditor( false );
1683
1684 if ( editor ) {
1685 editor._.previousScrollTop = null;
1686 }
1687 }
1688
1629 var win = parent.getWindow(); 1689 var win = parent.getWindow();
1630 1690
1631 var thisPos = screenPos( this, win ), 1691 var thisPos = screenPos( this, win ),
@@ -1797,7 +1857,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1797 this.getParent( true ) && this.$.parentNode.replaceChild( newNode.$, this.$ ); 1857 this.getParent( true ) && this.$.parentNode.replaceChild( newNode.$, this.$ );
1798 newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ]; 1858 newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ];
1799 this.$ = newNode.$; 1859 this.$ = newNode.$;
1800 // Bust getName's cache. (#8663) 1860 // Bust getName's cache. (http://dev.ckeditor.com/ticket/8663)
1801 delete this.getName; 1861 delete this.getName;
1802 }, 1862 },
1803 1863
@@ -1905,17 +1965,32 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
1905 * CKEDITOR.replace( element ); 1965 * CKEDITOR.replace( element );
1906 * alert( element.getEditor().name ); // 'editor1' 1966 * alert( element.getEditor().name ); // 'editor1'
1907 * 1967 *
1968 * By default this method considers only original DOM elements upon which the editor
1969 * was created. Setting `optimized` parameter to `false` will consider editor editable
1970 * and its children.
1971 *
1972 * @param {Boolean} [optimized=true] If set to `false` it will scan every editor editable.
1908 * @returns {CKEDITOR.editor} An editor instance or null if nothing has been found. 1973 * @returns {CKEDITOR.editor} An editor instance or null if nothing has been found.
1909 */ 1974 */
1910 getEditor: function() { 1975 getEditor: function( optimized ) {
1911 var instances = CKEDITOR.instances, 1976 var instances = CKEDITOR.instances,
1912 name, instance; 1977 name, instance, editable;
1978
1979 optimized = optimized || optimized === undefined;
1913 1980
1914 for ( name in instances ) { 1981 for ( name in instances ) {
1915 instance = instances[ name ]; 1982 instance = instances[ name ];
1916 1983
1917 if ( instance.element.equals( this ) && instance.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) 1984 if ( instance.element.equals( this ) && instance.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO )
1918 return instance; 1985 return instance;
1986
1987 if ( !optimized ) {
1988 editable = instance.editable();
1989
1990 if ( editable && ( editable.equals( this ) || editable.contains( this ) ) ) {
1991 return instance;
1992 }
1993 }
1919 } 1994 }
1920 1995
1921 return null; 1996 return null;
@@ -2038,7 +2113,8 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
2038 } 2113 }
2039 2114
2040 function getContextualizedSelector( element, selector ) { 2115 function getContextualizedSelector( element, selector ) {
2041 return '#' + element.$.id + ' ' + selector.split( /,\s*/ ).join( ', #' + element.$.id + ' ' ); 2116 var id = CKEDITOR.tools.escapeCss( element.$.id );
2117 return '#' + id + ' ' + selector.split( /,\s*/ ).join( ', #' + id + ' ' );
2042 } 2118 }
2043 2119
2044 var sides = { 2120 var sides = {
@@ -2070,7 +2146,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
2070 function marginAndPaddingSize( type ) { 2146 function marginAndPaddingSize( type ) {
2071 var adjustment = 0; 2147 var adjustment = 0;
2072 for ( var i = 0, len = sides[ type ].length; i < len; i++ ) 2148 for ( var i = 0, len = sides[ type ].length; i < len; i++ )
2073 adjustment += parseInt( this.getComputedStyle( sides[ type ][ i ] ) || 0, 10 ) || 0; 2149 adjustment += parseFloat( this.getComputedStyle( sides[ type ][ i ] ) || 0, 10 ) || 0;
2074 return adjustment; 2150 return adjustment;
2075 } 2151 }
2076 2152