X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fselection%2Fplugin.js;h=39c6db7d76f77510f4ffd627ce136cde2f049f8e;hb=e73319a12b56100b29ef456fd74114fe5519e01c;hp=66161f3b745726941e3e79db294c3f6a71c55c09;hpb=4e90e78dc97789709ee7404359a5517540c27553;p=ckeditor.git diff --git a/_source/plugins/selection/plugin.js b/_source/plugins/selection/plugin.js index 66161f3..39c6db7 100644 --- a/_source/plugins/selection/plugin.js +++ b/_source/plugins/selection/plugin.js @@ -78,6 +78,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license && node.getName() in CKEDITOR.dtd.$removeEmpty; } + function singletonBlock( node ) + { + var body = range.document.getBody(); + return !node.is( 'body' ) && body.getChildCount() == 1; + } + var start = range.startContainer, offset = range.startOffset; @@ -86,12 +92,15 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // 1. Empty inline element. ^ // 2. Adjoin to inline element.

text^

- return !CKEDITOR.tools.trim( start.getHtml() ) ? isInlineCt( start ) : isInlineCt( start.getChild( offset - 1 ) ) || isInlineCt( start.getChild( offset ) ); + // 3. The only empty block in document.

^

(#7222) + return !CKEDITOR.tools.trim( start.getHtml() ) ? isInlineCt( start ) || singletonBlock( start ) + : isInlineCt( start.getChild( offset - 1 ) ) || isInlineCt( start.getChild( offset ) ); } var selectAllCmd = { modes : { wysiwyg : 1, source : 1 }, + readOnly : CKEDITOR.env.ie || CKEDITOR.env.webkit, exec : function( editor ) { switch ( editor.mode ) @@ -454,6 +463,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license doc.on( 'mouseup', checkSelectionChangeTimeout, editor ); doc.on( 'keyup', checkSelectionChangeTimeout, editor ); + doc.on( 'selectionchange', checkSelectionChangeTimeout, editor ); } }); @@ -468,15 +478,22 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }); editor.selectionChange = checkSelectionChangeTimeout; + + // IE9 might cease to work if there's an object selection inside the iframe (#7639). + CKEDITOR.env.ie9Compat && editor.on( 'destroy', function() + { + var sel = editor.getSelection(); + sel && sel.getNative().clear(); + }, null, null, 9 ); } }); /** * Gets the current selection from the editing area when in WYSIWYG mode. - * @returns {CKEDITOR.dom.selection} A selection object or null if not on + * @returns {CKEDITOR.dom.selection} A selection object or null if not in * WYSIWYG mode or no selection is available. * @example - * var selection = CKEDITOR.instances.editor1.getSelection(); + * var selection = CKEDITOR.instances.editor1.getSelection(); * alert( selection.getType() ); */ CKEDITOR.editor.prototype.getSelection = function() @@ -493,7 +510,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * Gets the current selection from the document. * @returns {CKEDITOR.dom.selection} A selection object. * @example - * var selection = CKEDITOR.instances.editor1.document.getSelection(); + * var selection = CKEDITOR.instances.editor1.document.getSelection(); * alert( selection.getType() ); */ CKEDITOR.dom.document.prototype.getSelection = function() @@ -512,11 +529,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license CKEDITOR.SELECTION_NONE = 1; /** - * Text or collapsed selection. + * A text or a collapsed selection. * @constant * @example * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT ) - * alert( 'Text is selected' ); + * alert( 'A text is selected' ); */ CKEDITOR.SELECTION_TEXT = 2; @@ -532,7 +549,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /** * Manipulates the selection in a DOM document. * @constructor + * @param {CKEDITOR.dom.document} document The DOM document that contains the selection. * @example + * var sel = new CKEDITOR.dom.selection( CKEDITOR.document ); */ CKEDITOR.dom.selection = function( document ) { @@ -550,7 +569,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /** * IE BUG: The selection's document may be a different document than the - * editor document. Return null if that's the case. + * editor document. Return null if that is the case. */ if ( CKEDITOR.env.ie ) { @@ -567,19 +586,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }; var styleObjectElements = - { - img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1, - a:1, input:1, form:1, select:1, textarea:1, button:1, fieldset:1, th:1, thead:1, tfoot:1 - }; + { + img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1, + a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1 + }; CKEDITOR.dom.selection.prototype = { /** * Gets the native selection object from the browser. * @function - * @returns {Object} The native selection object. + * @returns {Object} The native browser selection object. * @example - * var selection = editor.getSelection().getNative(); + * var selection = editor.getSelection().getNative(); */ getNative : CKEDITOR.env.ie ? @@ -597,19 +616,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * Gets the type of the current selection. The following values are * available: * * @function * @returns {Number} One of the following constant values: - * {@link CKEDITOR.SELECTION_NONE}, {@link CKEDITOR.SELECTION_TEXT} or - * {@link CKEDITOR.SELECTION_ELEMENT}. + * {@link CKEDITOR.SELECTION_NONE}, {@link CKEDITOR.SELECTION_TEXT}, or + * {@link CKEDITOR.SELECTION_ELEMENT}. * @example - * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT ) - * alert( 'Text is selected' ); + * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT ) + * alert( 'A text is selected' ); */ getType : CKEDITOR.env.ie ? @@ -677,13 +696,15 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Retrieve the {@link CKEDITOR.dom.range} instances that represent the current selection. - * Note: Some browsers returns multiple ranges even on a sequent selection, e.g. Firefox returns - * one range for each table cell when one or more table row is selected. - * @return {Array} + * Retrieves the {@link CKEDITOR.dom.range} instances that represent the current selection. + * Note: Some browsers return multiple ranges even for a continuous selection. Firefox, for example, returns + * one range for each table cell when one or more table rows are selected. + * @function + * @param {Boolean} [onlyEditables] If set to true, this function retrives editable ranges only. + * @return {Array} Range instances that represent the current selection. * @example - * var ranges = selection.getRanges(); - * alert(ranges.length); + * var ranges = selection.getRanges(); + * alert( ranges.length ); */ getRanges : (function() { @@ -941,18 +962,30 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( range.collapsed ) continue; + // Range may start inside a non-editable element, + // replace the range start after it. + if ( range.startContainer.isReadOnly() ) + { + var current = range.startContainer; + while( current ) + { + if ( current.is( 'body' ) || !current.isReadOnly() ) + break; + + if ( current.type == CKEDITOR.NODE_ELEMENT + && current.getAttribute( 'contentEditable' ) == 'false' ) + range.setStartAfter( current ); + + current = current.getParent(); + } + } + var startContainer = range.startContainer, endContainer = range.endContainer, startOffset = range.startOffset, endOffset = range.endOffset, walkerRange = range.clone(); - // Range may start inside a non-editable element, restart range - // by the end of it. - var readOnly; - if ( ( readOnly = startContainer.isReadOnly() ) ) - range.setStartAfter( readOnly ); - // Enlarge range start/end with text node to avoid walker // being DOM destructive, it doesn't interfere our checking // of elements below as well. @@ -1015,7 +1048,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * @returns {CKEDITOR.dom.element} The element at the beginning of the * selection. * @example - * var element = editor.getSelection().getStartElement(); + * var element = editor.getSelection().getStartElement(); * alert( element.getName() ); */ getStartElement : function() @@ -1091,12 +1124,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Gets the current selected element. + * Gets the currently selected element. * @returns {CKEDITOR.dom.element} The selected element. Null if no * selection is available or the selection type is not - * {@link CKEDITOR.SELECTION_ELEMENT}. + * {@link CKEDITOR.SELECTION_ELEMENT}. * @example - * var element = editor.getSelection().getSelectedElement(); + * var element = editor.getSelection().getSelectedElement(); * alert( element.getName() ); */ getSelectedElement : function() @@ -1113,6 +1146,74 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { return self.getNative().createRange().item( 0 ); }, + // If a table or list is fully selected. + function() + { + var root, + retval, + range = self.getRanges()[ 0 ], + ancestor = range.getCommonAncestor( 1, 1 ), + tags = { table:1,ul:1,ol:1,dl:1 }; + + for ( var t in tags ) + { + if ( root = ancestor.getAscendant( t, 1 ) ) + break; + } + + if ( root ) + { + // Enlarging the start boundary. + var testRange = new CKEDITOR.dom.range( this.document ); + testRange.setStartAt( root, CKEDITOR.POSITION_AFTER_START ); + testRange.setEnd( range.startContainer, range.startOffset ); + + var enlargeables = CKEDITOR.tools.extend( tags, CKEDITOR.dtd.$listItem, CKEDITOR.dtd.$tableContent ), + walker = new CKEDITOR.dom.walker( testRange ), + // Check the range is at the inner boundary of the structural element. + guard = function( walker, isEnd ) + { + return function( node, isWalkOut ) + { + if ( node.type == CKEDITOR.NODE_TEXT && ( !CKEDITOR.tools.trim( node.getText() ) || node.getParent().data( 'cke-bookmark' ) ) ) + return true; + + var tag; + if ( node.type == CKEDITOR.NODE_ELEMENT ) + { + tag = node.getName(); + + // Bypass bogus br at the end of block. + if ( tag == 'br' && isEnd && node.equals( node.getParent().getBogus() ) ) + return true; + + if ( isWalkOut && tag in enlargeables || tag in CKEDITOR.dtd.$removeEmpty ) + return true; + } + + walker.halted = 1; + return false; + }; + }; + + walker.guard = guard( walker ); + + if ( walker.checkBackward() && !walker.halted ) + { + walker = new CKEDITOR.dom.walker( testRange ); + testRange.setStart( range.endContainer, range.endOffset ); + testRange.setEndAt( root, CKEDITOR.POSITION_BEFORE_END ); + walker.guard = guard( walker, 1 ); + if ( walker.checkForward() && !walker.halted ) + retval = root.$; + } + } + + if ( !retval ) + throw 0; + + return retval; + }, // Figure it out by checking if there's a single enclosed // node of the range. function() @@ -1137,12 +1238,43 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null ); }, + /** + * Retrieves the text contained within the range. An empty string is returned for non-text selection. + * @returns {String} A string of text within the current selection. + * @since 3.6.1 + * @example + * var text = editor.getSelection().getSelectedText(); + * alert( text ); + */ + getSelectedText : function() + { + var cache = this._.cache; + if ( cache.selectedText !== undefined ) + return cache.selectedText; + + var text = '', + nativeSel = this.getNative(); + if ( this.getType() == CKEDITOR.SELECTION_TEXT ) + text = CKEDITOR.env.ie ? nativeSel.createRange().text : nativeSel.toString(); + + return ( cache.selectedText = text ); + }, + + /** + * Locks the selection made in the editor in order to make it possible to + * manipulate it without browser interference. A locked selection is + * cached and remains unchanged until it is released with the #unlock + * method. + * @example + * editor.getSelection().lock(); + */ lock : function() { // Call all cacheable function. this.getRanges(); this.getStartElement(); this.getSelectedElement(); + this.getSelectedText(); // The native selection is not available when locked. this._.cache.nativeSel = {}; @@ -1153,6 +1285,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license this.document.setCustomData( 'cke_locked_selection', this ); }, + /** + * Unlocks the selection made in the editor and locked with the #lock method. + * An unlocked selection is no longer cached and can be changed. + * @param {Boolean} [restore] If set to true, the selection is restored back to the selection saved earlier by using the #lock method. + * @example + * editor.getSelection().unlock(); + */ unlock : function( restore ) { var doc = this.document, @@ -1186,14 +1325,22 @@ For licensing, see LICENSE.html or http://ckeditor.com/license } }, + /** + * Clears the selection cache. + * @example + * editor.getSelection().reset(); + */ reset : function() { this._.cache = {}; }, /** - * Make the current selection of type {@link CKEDITOR.SELECTION_ELEMENT} by enclosing the specified element. - * @param element + * Makes the current selection of type {@link CKEDITOR.SELECTION_ELEMENT} by enclosing the specified element. + * @param {CKEDITOR.dom.element} element The element to enclose in the selection. + * @example + * var element = editor.document.getById( 'sampleElement' ); + * editor.getSelection.selectElement( element ); */ selectElement : function( element ) { @@ -1222,9 +1369,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Adding the specified ranges to document selection preceding - * by clearing up the original selection. - * @param {CKEDITOR.dom.range} ranges + * Clears the original selection and adds the specified ranges + * to the document selection. + * @param {Array} ranges An array of {@link CKEDITOR.dom.range} instances representing ranges to be added to the document. + * @example + * var ranges = new CKEDITOR.dom.range( editor.document ); + * editor.getSelection().selectRanges( [ ranges ] ); */ selectRanges : function( ranges ) { @@ -1363,15 +1513,20 @@ For licensing, see LICENSE.html or http://ckeditor.com/license sel.addRange( nativeRange ); } + // Don't miss selection change event for non-IEs. + this.document.fire( 'selectionchange' ); this.reset(); } }, /** - * Create bookmark for every single of this selection range (from #getRanges) - * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark} method, - * with extra cares to avoid interferon among those ranges. Same arguments are - * received as with the underlay range method. + * Creates a bookmark for each range of this selection (from #getRanges) + * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark} method, + * with extra care taken to avoid interference among those ranges. The arguments + * received are the same as with the underlying range method. + * @returns {Array} Array of bookmarks for each range. + * @example + * var bookmarks = editor.getSelection().createBookmarks(); */ createBookmarks : function( serializable ) { @@ -1379,10 +1534,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Create bookmark for every single of this selection range (from #getRanges) - * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark2} method, - * with extra cares to avoid interferon among those ranges. Same arguments are - * received as with the underlay range method. + * Creates a bookmark for each range of this selection (from #getRanges) + * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark2} method, + * with extra care taken to avoid interference among those ranges. The arguments + * received are the same as with the underlying range method. + * @returns {Array} Array of bookmarks for each range. + * @example + * var bookmarks = editor.getSelection().createBookmarks2(); */ createBookmarks2 : function( normalized ) { @@ -1390,8 +1548,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Select the virtual ranges denote by the bookmarks by calling #selectRanges. - * @param bookmarks + * Selects the virtual ranges denoted by the bookmarks by calling #selectRanges. + * @param {Array} bookmarks The bookmarks representing ranges to be selected. + * @returns {CKEDITOR.dom.selection} This selection object, after the ranges were selected. + * @example + * var bookmarks = editor.getSelection().createBookmarks(); + * editor.getSelection().selectBookmarks( bookmarks ); */ selectBookmarks : function( bookmarks ) { @@ -1407,7 +1569,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Retrieve the common ancestor node of the first range and the last range. + * Retrieves the common ancestor node of the first range and the last range. + * @returns {CKEDITOR.dom.element} The common ancestor of the selection. + * @example + * var ancestor = editor.getSelection().getCommonAncestor(); */ getCommonAncestor : function() { @@ -1418,7 +1583,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** - * Moving scroll bar to the current selection's start position. + * Moves the scrollbar to the starting position of the current selection. + * @example + * editor.getSelection().scrollIntoView(); */ scrollIntoView : function() {