diff options
Diffstat (limited to '_source/core/dom')
| -rw-r--r-- | _source/core/dom/comment.js | 32 | ||||
| -rw-r--r-- | _source/core/dom/document.js | 17 | ||||
| -rw-r--r-- | _source/core/dom/documentfragment.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/domobject.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/element.js | 89 | ||||
| -rw-r--r-- | _source/core/dom/elementpath.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/event.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/node.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/nodelist.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/range.js | 131 | ||||
| -rw-r--r-- | _source/core/dom/text.js | 2 | ||||
| -rw-r--r-- | _source/core/dom/walker.js | 50 | ||||
| -rw-r--r-- | _source/core/dom/window.js | 2 |
13 files changed, 260 insertions, 75 deletions
diff --git a/_source/core/dom/comment.js b/_source/core/dom/comment.js new file mode 100644 index 0000000..9e9c9b7 --- /dev/null +++ b/_source/core/dom/comment.js @@ -0,0 +1,32 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview Defines the {@link CKEDITOR.dom.comment} class, which represents + * a DOM comment node. + */ + +CKEDITOR.dom.comment = CKEDITOR.tools.createClass( +{ + base : CKEDITOR.dom.node, + + $ : function( text, ownerDocument ) + { + if ( typeof text == 'string' ) + text = ( ownerDocument ? ownerDocument.$ : document ).createComment( text ); + + this.base( text ); + }, + + proto : + { + type : CKEDITOR.NODE_COMMENT, + + getOuterHtml : function() + { + return '<!--' + this.$.nodeValue + '-->'; + } + } +}); diff --git a/_source/core/dom/document.js b/_source/core/dom/document.js index dfe9a3c..0575338 100644 --- a/_source/core/dom/document.js +++ b/_source/core/dom/document.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -52,6 +52,21 @@ CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, } }, + appendStyleText : function( cssStyleText ) + { + if ( this.$.createStyleSheet ) + { + var styleSheet = this.$.createStyleSheet( "" ); + styleSheet.cssText = cssStyleText ; + } + else + { + var style = new CKEDITOR.dom.element( 'style', this ); + style.append( new CKEDITOR.dom.text( cssStyleText, this ) ); + this.getHead().append( style ); + } + }, + createElement : function( name, attribsAndStyles ) { var element = new CKEDITOR.dom.element( name, this ); diff --git a/_source/core/dom/documentfragment.js b/_source/core/dom/documentfragment.js index ed04202..69bc70e 100644 --- a/_source/core/dom/documentfragment.js +++ b/_source/core/dom/documentfragment.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ /** diff --git a/_source/core/dom/domobject.js b/_source/core/dom/domobject.js index 983f523..a2964b8 100644 --- a/_source/core/dom/domobject.js +++ b/_source/core/dom/domobject.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/element.js b/_source/core/dom/element.js index 5a96a0f..2f58326 100644 --- a/_source/core/dom/element.js +++ b/_source/core/dom/element.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -256,15 +256,15 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, * @param {CKEDITOR.dom.element} parent The anscestor element to get broken. * @example * // Before breaking: - * // <b>This <i>is some<span /> sample</i> test text</b> - * // If "element" is <span /> and "parent" is <i>: - * // <b>This <i>is some</i><span /><i> sample</i> test text</b> + * // <b>This <i>is some<span /> sample</i> test text</b> + * // If "element" is <span /> and "parent" is <i>: + * // <b>This <i>is some</i><span /><i> sample</i> test text</b> * element.breakParent( parent ); * @example * // Before breaking: - * // <b>This <i>is some<span /> sample</i> test text</b> - * // If "element" is <span /> and "parent" is <b>: - * // <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b> + * // <b>This <i>is some<span /> sample</i> test text</b> + * // If "element" is <span /> and "parent" is <b>: + * // <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b> * element.breakParent( parent ); */ breakParent : function( parent ) @@ -328,7 +328,9 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, */ getHtml : function() { - return this.$.innerHTML; + var retval = this.$.innerHTML; + // Strip <?xml:namespace> tags in IE. (#3341). + return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval; }, getOuterHtml : function() @@ -425,8 +427,16 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, break; case 'checked': - return this.$.checked; - break; + { + var attr = this.$.attributes.getNamedItem( name ), + attrValue = attr.specified ? attr.nodeValue // For value given by parser. + : this.$.checked; // For value created via DOM interface. + + return attrValue ? 'checked' : null; + } + + case 'hspace': + return this.$.hspace; case 'style': // IE does not return inline styles via getAttribute(). See #2947. @@ -627,6 +637,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, /** * Gets the first child node of this element. + * @param {Function} evaluator Filtering the result node. * @returns {CKEDITOR.dom.node} The first child node or null if not * available. * @example @@ -634,10 +645,14 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, * var first = <b>element.getFirst()</b>; * alert( first.getName() ); // "b" */ - getFirst : function() + getFirst : function( evaluator ) { - var $ = this.$.firstChild; - return $ ? new CKEDITOR.dom.node( $ ) : null; + var first = this.$.firstChild, + retval = first && new CKEDITOR.dom.node( first ); + if ( retval && evaluator && !evaluator( retval ) ) + retval = retval.getNext( evaluator ); + + return retval; }, /** @@ -722,8 +737,8 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, for ( i = 0 ; i < otherLength ; i++ ) { attribute = otherAttribs[ i ]; - - if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != thisAttribs.getAttribute( attribute.nodeName ) ) + if ( attribute.specified && attribute.nodeName != '_cke_expando' + && attribute.nodeValue != this.getAttribute( attribute.nodeName ) ) return false; } } @@ -739,7 +754,24 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, */ isVisible : function() { - return this.$.offsetWidth && ( this.$.style.visibility != 'hidden' ); + var isVisible = !!this.$.offsetHeight && this.getComputedStyle( 'visibility' ) != 'hidden', + elementWindow, + elementWindowFrame; + + // Webkit and Opera report non-zero offsetHeight despite that + // element is inside an invisible iframe. (#4542) + if ( isVisible && ( CKEDITOR.env.webkit || CKEDITOR.env.opera ) ) + { + elementWindow = this.getWindow(); + + if ( !elementWindow.equals( CKEDITOR.document.getWindow() ) + && ( elementWindowFrame = elementWindow.$.frameElement ) ) + { + isVisible = new CKEDITOR.dom.element( elementWindowFrame ).isVisible(); + } + } + + return isVisible; }, /** @@ -971,10 +1003,9 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, */ removeStyle : function( name ) { + this.setStyle( name, '' ); if ( this.$.style.removeAttribute ) this.$.style.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) ); - else - this.setStyle( name, '' ); if ( !this.$.style.cssText ) this.removeAttribute( 'style' ); @@ -1306,16 +1337,22 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, { var attribute = attributes[n]; + // Lowercase attribute name hard rule is broken for + // some attribute on IE, e.g. CHECKED. + var attrName = attribute.nodeName.toLowerCase(), + attrValue; + + // We can set the type only once, so do it with the proper value, not copying it. + if ( attrName in skipAttributes ) + continue; + + if( attrName == 'checked' && ( attrValue = this.getAttribute( attrName ) ) ) + dest.setAttribute( attrName, attrValue ); // IE BUG: value attribute is never specified even if it exists. - if ( attribute.specified || - ( CKEDITOR.env.ie && attribute.nodeValue && attribute.nodeName.toLowerCase() == 'value' ) ) + else if ( attribute.specified || + ( CKEDITOR.env.ie && attribute.nodeValue && attrName == 'value' ) ) { - var attrName = attribute.nodeName; - // We can set the type only once, so do it with the proper value, not copying it. - if ( attrName in skipAttributes ) - continue; - - var attrValue = this.getAttribute( attrName ); + attrValue = this.getAttribute( attrName ); if ( attrValue === null ) attrValue = attribute.nodeValue; diff --git a/_source/core/dom/elementpath.js b/_source/core/dom/elementpath.js index df75554..74641ae 100644 --- a/_source/core/dom/elementpath.js +++ b/_source/core/dom/elementpath.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/event.js b/_source/core/dom/event.js index 41cbd48..24b75d0 100644 --- a/_source/core/dom/event.js +++ b/_source/core/dom/event.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/node.js b/_source/core/dom/node.js index a32e45a..24e3894 100644 --- a/_source/core/dom/node.js +++ b/_source/core/dom/node.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/nodelist.js b/_source/core/dom/nodelist.js index c0f4087..155f0ad 100644 --- a/_source/core/dom/nodelist.js +++ b/_source/core/dom/nodelist.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/range.js b/_source/core/dom/range.js index cd7ba9d..d3ad684 100644 --- a/_source/core/dom/range.js +++ b/_source/core/dom/range.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -278,7 +278,7 @@ CKEDITOR.dom.range = function( document ) if ( CKEDITOR.tools.trim( node.getText() ).length ) return false; } - else + else if( node.type == CKEDITOR.NODE_ELEMENT ) { // If there are non-empty inline elements (e.g. <img />), then we're not // at the start. @@ -308,6 +308,15 @@ CKEDITOR.dom.range = function( document ) || node.getParent().hasAttribute( '_fck_bookmark' ); } + var whitespaceEval = new CKEDITOR.dom.walker.whitespaces(), + bookmarkEval = new CKEDITOR.dom.walker.bookmark(); + + function nonWhitespaceOrBookmarkEval( node ) + { + // Whitespaces and bookmark nodes are to be ignored. + return !whitespaceEval( node ) && !bookmarkEval( node ); + } + CKEDITOR.dom.range.prototype = { clone : function() @@ -1183,7 +1192,8 @@ CKEDITOR.dom.range = function( document ) this.setStartAt( blockBoundary, !blockBoundary.is( 'br' ) && - ( !enlargeable || blockBoundary.contains( enlargeable ) ) ? + ( !enlargeable && this.checkStartOfBlock() + || enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END ); @@ -1208,8 +1218,8 @@ CKEDITOR.dom.range = function( document ) // the document position of it with 'enlargeable' node. this.setEndAt( blockBoundary, - !blockBoundary.is( 'br' ) && - ( !enlargeable || blockBoundary.contains( enlargeable ) ) ? + ( !enlargeable && this.checkEndOfBlock() + || enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_BEFORE_END : CKEDITOR.POSITION_BEFORE_START ); // We must include the <br> at the end of range if there's @@ -1457,18 +1467,7 @@ CKEDITOR.dom.range = function( document ) } else { - // Extract the contents of the block from the selection point to the end - // of its contents. - this.setEndAt( startBlock, CKEDITOR.POSITION_BEFORE_END ); - var documentFragment = this.extractContents(); - - // Duplicate the block element after it. - endBlock = startBlock.clone( false ); - - // Place the extracted contents into the duplicated block. - documentFragment.appendTo( endBlock ); - endBlock.insertAfter( startBlock ); - this.moveToPosition( startBlock, CKEDITOR.POSITION_AFTER_END ); + endBlock = this.splitElement( startBlock ); // In Gecko, the last child node must be a bogus <br>. // Note: bogus <br> added under <ul> or <ol> would cause @@ -1488,6 +1487,33 @@ CKEDITOR.dom.range = function( document ) }, /** + * Branch the specified element from the collapsed range position and + * place the caret between the two result branches. + * Note: The range must be collapsed and been enclosed by this element. + * @param {CKEDITOR.dom.element} element + * @return {CKEDITOR.dom.element} Root element of the new branch after the split. + */ + splitElement : function( toSplit ) + { + if ( !this.collapsed ) + return null; + + // Extract the contents of the block from the selection point to the end + // of its contents. + this.setEndAt( toSplit, CKEDITOR.POSITION_BEFORE_END ); + var documentFragment = this.extractContents(); + + // Duplicate the element after it. + var clone = toSplit.clone( false ); + + // Place the extracted contents into the duplicated element. + documentFragment.appendTo( clone ); + clone.insertAfter( toSplit ); + this.moveToPosition( toSplit, CKEDITOR.POSITION_AFTER_END ); + return clone; + }, + + /** * Check whether current range is on the inner edge of the specified element. * @param {Number} checkType ( CKEDITOR.START | CKEDITOR.END ) The checking side. * @param {CKEDITOR.dom.element} element The target element to check. @@ -1579,34 +1605,69 @@ CKEDITOR.dom.range = function( document ) }, /** - * Moves the range boundaries to the first editing point inside an + * Moves the range boundaries to the first/end editing point inside an * element. For example, in an element tree like * "<p><b><i></i></b> Text</p>", the start editing point is * "<p><b><i>^</i></b> Text</p>" (inside <i>). - * @param {CKEDITOR.dom.element} targetElement The element into which - * look for the editing spot. + * @param {CKEDITOR.dom.element} el The element into which look for the + * editing spot. + * @param {Boolean} isMoveToEnd Whether move to the end editable position. */ - moveToElementEditStart : function( targetElement ) + moveToElementEditablePosition : function( el, isMoveToEnd ) { - var editableElement; + var isEditable; - while ( targetElement && targetElement.type == CKEDITOR.NODE_ELEMENT ) + while ( el && el.type == CKEDITOR.NODE_ELEMENT ) { - if ( targetElement.isEditable() ) - editableElement = targetElement; - else if ( editableElement ) - break ; // If we already found an editable element, stop the loop. + isEditable = el.isEditable(); - targetElement = targetElement.getFirst(); - } + // If an editable element is found, move inside it. + if ( isEditable ) + this.moveToPosition( el, isMoveToEnd ? + CKEDITOR.POSITION_BEFORE_END : + CKEDITOR.POSITION_AFTER_START ); + // Stop immediately if we've found a non editable inline element (e.g <img>). + else if ( CKEDITOR.dtd.$inline[ el.getName() ] ) + { + this.moveToPosition( el, isMoveToEnd ? + CKEDITOR.POSITION_AFTER_END : + CKEDITOR.POSITION_BEFORE_START ); + return true; + } - if ( editableElement ) - { - this.moveToPosition(editableElement, CKEDITOR.POSITION_AFTER_START); - return true; + // Non-editable non-inline elements are to be bypassed, getting the next one. + if ( CKEDITOR.dtd.$empty[ el.getName() ] ) + el = el[ isMoveToEnd ? 'getPrevious' : 'getNext' ]( nonWhitespaceOrBookmarkEval ); + else + el = el[ isMoveToEnd ? 'getLast' : 'getFirst' ]( nonWhitespaceOrBookmarkEval ); + + // Stop immediately if we've found a text node. + if ( el && el.type == CKEDITOR.NODE_TEXT ) + { + this.moveToPosition( el, isMoveToEnd ? + CKEDITOR.POSITION_AFTER_END : + CKEDITOR.POSITION_BEFORE_START ); + return true; + } } - else - return false; + + return isEditable; + }, + + /** + *@see {CKEDITOR.dom.range.moveToElementEditablePosition} + */ + moveToElementEditStart : function( target ) + { + return this.moveToElementEditablePosition( target ); + }, + + /** + *@see {CKEDITOR.dom.range.moveToElementEditablePosition} + */ + moveToElementEditEnd : function( target ) + { + return this.moveToElementEditablePosition( target, true ); }, /** diff --git a/_source/core/dom/text.js b/_source/core/dom/text.js index 1f8b192..407c47f 100644 --- a/_source/core/dom/text.js +++ b/_source/core/dom/text.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ diff --git a/_source/core/dom/walker.js b/_source/core/dom/walker.js index 766eaa2..2010078 100644 --- a/_source/core/dom/walker.js +++ b/_source/core/dom/walker.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -47,7 +47,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { return ( ( !movingOut || !limitLTR.equals( node ) ) && ( !blockerLTR || !node.equals( blockerLTR ) ) - && ( node.type != CKEDITOR.NODE_ELEMENT || node.getName() != 'body' ) ); + && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || node.getName() != 'body' ) ); }; } @@ -62,7 +62,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { return ( ( !movingOut || !limitRTL.equals( node ) ) && ( !blockerRTL || !node.equals( blockerRTL ) ) - && ( node.type != CKEDITOR.NODE_ELEMENT || node.getName() != 'body' ) ); + && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || node.getName() != 'body' ) ); }; } @@ -78,7 +78,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( stopGuard( node, movingOut ) === false ) return false; - return userGuard( node ); + return userGuard( node, movingOut ); }; } else @@ -396,7 +396,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }; /** - * Whether the node contains only white-spaces characters. + * Whether the node is a text node containing only whitespaces characters. * @param isReject */ CKEDITOR.dom.walker.whitespaces = function( isReject ) @@ -408,4 +408,44 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return isReject ^ isWhitespace; }; }; + + /** + * Whether the node is invisible in wysiwyg mode. + * @param isReject + */ + CKEDITOR.dom.walker.invisible = function( isReject ) + { + var whitespace = CKEDITOR.dom.walker.whitespaces(); + return function( node ) + { + // Nodes that take no spaces in wysiwyg: + // 1. White-spaces but not including NBSP; + // 2. Empty inline elements, e.g. <b></b> we're checking here + // 'offsetHeight' instead of 'offsetWidth' for properly excluding + // all sorts of empty paragraph, e.g. <br />. + var isInvisible = whitespace( node ) || node.is && !node.$.offsetHeight; + return isReject ^ isInvisible; + }; + }; + + var tailNbspRegex = /^[\t\r\n ]*(?: |\xa0)$/, + isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ), + isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ), + fillerEvaluator = function( element ) + { + return isNotBookmark( element ) && isNotWhitespaces( element ); + }; + + // Check if there's a filler node at the end of an element, and return it. + CKEDITOR.dom.element.prototype.getBogus = function () + { + var tail = this.getLast( fillerEvaluator ); + if ( tail && ( !CKEDITOR.env.ie ? tail.is && tail.is( 'br' ) + : tail.getText && tailNbspRegex.test( tail.getText() ) ) ) + { + return tail; + } + return false; + }; + })(); diff --git a/_source/core/dom/window.js b/_source/core/dom/window.js index 813a799..c9fb002 100644 --- a/_source/core/dom/window.js +++ b/_source/core/dom/window.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ |
