X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fselection%2Fplugin.js;h=fd8332e0a04a2925d00e9a4dba166a1168210942;hb=8f6c203fdaa543c3bca40baea6ae4ddcdf1a77f5;hp=f0d2c4456ffa10d94a892dc55aea9f01df9d69ac;hpb=c9fdde67e6384bd5a66adc2b3bba5c4ce9db56c7;p=ckeditor.git diff --git a/_source/plugins/selection/plugin.js b/_source/plugins/selection/plugin.js index f0d2c44..fd8332e 100644 --- a/_source/plugins/selection/plugin.js +++ b/_source/plugins/selection/plugin.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -79,20 +79,21 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { case 'wysiwyg' : editor.document.$.execCommand( 'SelectAll', false, null ); + // Force triggering selectionChange (#7008) + editor.forceNextSelectionCheck(); + editor.selectionChange(); break; case 'source' : // Select the contents of the textarea - var textarea = editor.textarea.$ ; + var textarea = editor.textarea.$; if ( CKEDITOR.env.ie ) - { - textarea.createTextRange().execCommand( 'SelectAll' ) ; - } + textarea.createTextRange().execCommand( 'SelectAll' ); else { - textarea.selectionStart = 0 ; - textarea.selectionEnd = textarea.value.length ; + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; } - textarea.focus() ; + textarea.focus(); } }, canUndo : false @@ -133,7 +134,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // point. if ( savedRange ) { - if ( restoreEnabled ) + // Range restored here might invalidate the DOM structure thus break up + // the locked selection, give it up. (#6083) + var lockedSelection = doc.getCustomData( 'cke_locked_selection' ); + if ( restoreEnabled && !lockedSelection ) { // Well not break because of this. try @@ -151,7 +155,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license body.on( 'focus', function() { // Enable selections to be saved. - saveEnabled = true; + saveEnabled = 1; saveSelection(); }); @@ -164,7 +168,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return; // Disable selections from being saved. - saveEnabled = false; + saveEnabled = 0; restoreEnabled = 1; }); @@ -174,13 +178,18 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { editor.on( 'blur', function( evt ) { - editor.document && editor.document.$.selection.empty(); + // Try/Catch to avoid errors if the editor is hidden. (#6375) + try + { + editor.document && editor.document.$.selection.empty(); + } + catch (e) {} }); } // Listening on document element ensures that // scrollbar is included. (#5280) - html.on( 'mousedown', function () + html.on( 'mousedown', function() { // Lock restore selection now, as we have // a followed 'click' event which introduce @@ -188,7 +197,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license restoreEnabled = 0; }); - html.on( 'mouseup', function () + html.on( 'mouseup', function() { restoreEnabled = 1; }); @@ -235,7 +244,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license } scroll = null; - saveEnabled = true; + saveEnabled = 1; setTimeout( function() { saveSelection( true ); @@ -247,7 +256,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license body.on( 'keyup', function() { - saveEnabled = true; + saveEnabled = 1; saveSelection(); }); @@ -258,7 +267,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license function disableSave() { - saveEnabled = false; + saveEnabled = 0; } function saveSelection( testIt ) @@ -318,6 +327,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license } }); + // Clear the cached range path before unload. (#7174) + editor.on( 'contentDomUnload', editor.forceNextSelectionCheck, editor ); + editor.addCommand( 'selectAll', selectAllCmd ); editor.ui.addButton( 'SelectAll', { @@ -400,7 +412,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return lockedSelection; this.document = document; - this.isLocked = false; + this.isLocked = 0; this._ = { cache : {} @@ -543,11 +555,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * var ranges = selection.getRanges(); * alert(ranges.length); */ - getRanges : (function () + getRanges : (function() { var func = CKEDITOR.env.ie ? ( function() { + function getNodeIndex( node ) { return new CKEDITOR.dom.node( node ).getIndex(); } + // Finds the container and offset for a specific boundary // of an IE range. var getBoundaryInformation = function( range, start ) @@ -558,76 +572,103 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // Gets the element that encloses the range entirely. var parent = range.parentElement(); - var siblings = parent.childNodes; - var testRange; - - for ( var i = 0 ; i < siblings.length ; i++ ) + // Empty parent element, e.g. ^ + if ( !parent.hasChildNodes() ) + return { container : parent, offset : 0 }; + + var siblings = parent.children, + child, + testRange = range.duplicate(), + startIndex = 0, + endIndex = siblings.length - 1, + index = -1, + position, + distance; + + // Binary search over all element childs to test the range to see whether + // range is right on the boundary of one element. + while ( startIndex <= endIndex ) { - var child = siblings[ i ]; - if ( child.nodeType == 1 ) - { - testRange = range.duplicate(); + index = Math.floor( ( startIndex + endIndex ) / 2 ); + child = siblings[ index ]; + testRange.moveToElementText( child ); + position = testRange.compareEndPoints( 'StartToStart', range ); + + if ( position > 0 ) + endIndex = index - 1; + else if ( position < 0 ) + startIndex = index + 1; + else + return { container : parent, offset : getNodeIndex( child ) }; + } - testRange.moveToElementText( child ); + // All childs are text nodes, + // or to the right hand of test range are all text nodes. (#6992) + if ( index == -1 || index == siblings.length - 1 && position < 0 ) + { + // Adapt test range to embrace the entire parent contents. + testRange.moveToElementText( parent ); + testRange.setEndPoint( 'StartToStart', range ); - var comparisonStart = testRange.compareEndPoints( 'StartToStart', range ), - comparisonEnd = testRange.compareEndPoints( 'EndToStart', range ); + // IE report line break as CRLF with range.text but + // only LF with textnode.nodeValue, normalize them to avoid + // breaking character counting logic below. (#3949) + distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; - testRange.collapse(); + siblings = parent.childNodes; - if ( comparisonStart > 0 ) - break; - // When selection stay at the side of certain self-closing elements, e.g. BR, - // our comparison will never shows an equality. (#4824) - else if ( !comparisonStart - || comparisonEnd == 1 && comparisonStart == -1 ) - return { container : parent, offset : i }; - else if ( !comparisonEnd ) - return { container : parent, offset : i + 1 }; + // Actual range anchor right beside test range at the boundary of text node. + if ( !distance ) + { + child = siblings[ siblings.length - 1 ]; - testRange = null; + if ( child.nodeType == CKEDITOR.NODE_ELEMENT ) + return { container : parent, offset : siblings.length }; + else + return { container : child, offset : child.nodeValue.length }; } - } - - if ( !testRange ) - { - testRange = range.duplicate(); - testRange.moveToElementText( parent ); - testRange.collapse( false ); - } - - testRange.setEndPoint( 'StartToStart', range ); - // IE report line break as CRLF with range.text but - // only LF with textnode.nodeValue, normalize them to avoid - // breaking character counting logic below. (#3949) - var distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; - try - { + // Start the measuring until distance overflows, meanwhile count the text nodes. + var i = siblings.length; while ( distance > 0 ) distance -= siblings[ --i ].nodeValue.length; - } - // Measurement in IE could be somtimes wrong because of element. (#4611) + catch( e ) + { + return { container : parent, offset : getNodeIndex( child ) }; + } + } + + return { container : child, offset : position > 0 ? -distance : child.nodeValue.length + distance }; } }; @@ -655,6 +696,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license boundaryInfo = getBoundaryInformation( nativeRange ); range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); + // Correct an invalid IE range case on empty list item. (#5850) + if ( range.endContainer.getPosition( range.startContainer ) & CKEDITOR.POSITION_PRECEDING + && range.endOffset <= range.startContainer.getIndex() ) + { + range.collapse(); + } + return [ range ]; } else if ( type == CKEDITOR.SELECTION_ELEMENT ) @@ -740,7 +788,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // Drop range spans inside one ready-only node. var parent = range.getCommonAncestor(); - if ( parent.isReadOnly()) + if ( parent.isReadOnly() ) ranges.splice( i, 1 ); if ( range.collapsed ) @@ -782,7 +830,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license walker.evaluator = function( node ) { if ( node.type == CKEDITOR.NODE_ELEMENT - && node.getAttribute( 'contenteditable' ) == 'false' ) + && node.isReadOnly() ) { var newRange = range.clone(); range.setEndBefore( node ); @@ -850,7 +898,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // Decrease the range content to exclude particial // selected node on the start which doesn't have // visual impact. ( #3231 ) - while ( true ) + while ( 1 ) { var startContainer = range.startContainer, startOffset = range.startOffset; @@ -870,32 +918,25 @@ For licensing, see LICENSE.html or http://ckeditor.com/license node = node.getChild( range.startOffset ); if ( !node || node.type != CKEDITOR.NODE_ELEMENT ) - return range.startContainer; - - var child = node.getFirst(); - while ( child && child.type == CKEDITOR.NODE_ELEMENT ) + node = range.startContainer; + else { - node = child; - child = child.getFirst(); + var child = node.getFirst(); + while ( child && child.type == CKEDITOR.NODE_ELEMENT ) + { + node = child; + child = child.getFirst(); + } } - - return node; } - } - - if ( CKEDITOR.env.ie ) - { - range = sel.createRange(); - range.collapse( true ); - - node = range.parentElement(); - } - else - { - node = sel.anchorNode; + else + { + node = range.startContainer; + if ( node.type != CKEDITOR.NODE_ELEMENT ) + node = node.getParent(); + } - if ( node && node.nodeType != 1 ) - node = node.parentNode; + node = node.$; } } @@ -959,7 +1000,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // The native selection is not available when locked. this._.cache.nativeSel = {}; - this.isLocked = true; + this.isLocked = 1; // Save this selection inside the DOM document. this.document.setCustomData( 'cke_locked_selection', this ); @@ -979,7 +1020,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var selectedElement = lockedSelection.getSelectedElement(), ranges = !selectedElement && lockedSelection.getRanges(); - this.isLocked = false; + this.isLocked = 0; this.reset(); doc.getBody().focus(); @@ -993,7 +1034,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( !lockedSelection || !restore ) { - this.isLocked = false; + this.isLocked = 0; this.reset(); } }, @@ -1034,7 +1075,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license range.addElement( element.$ ); range.select(); } - catch(e) + catch( e ) { // If failed, select it as a text range. range = this.document.$.body.createTextRange(); @@ -1118,7 +1159,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( !between.collapsed ) { between.shrink( CKEDITOR.NODE_ELEMENT, true ); - if ( between.getCommonAncestor().isReadOnly()) + var ancestor = between.getCommonAncestor(), + enclosed = between.getEnclosedNode(); + + // The following cases has to be considered: + // 1. [placeholder] + // 2. (#6621) + if ( ancestor.isReadOnly() || enclosed && enclosed.isReadOnly() ) { right.setStart( left.startContainer, left.startOffset ); ranges.splice( i--, 1 ); @@ -1361,7 +1408,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license } var selection = this.document.getSelection().getNative(); - selection.removeAllRanges(); - selection.addRange( nativeRange ); + // getSelection() returns null in case when iframe is "display:none" in FF. (#6577) + if ( selection ) + { + selection.removeAllRanges(); + selection.addRange( nativeRange ); + } }; } )();