X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fselection%2Fplugin.js;h=39c6db7d76f77510f4ffd627ce136cde2f049f8e;hb=e73319a12b56100b29ef456fd74114fe5519e01c;hp=fd8332e0a04a2925d00e9a4dba166a1168210942;hpb=8f6c203fdaa543c3bca40baea6ae4ddcdf1a77f5;p=ckeditor.git
diff --git a/_source/plugins/selection/plugin.js b/_source/plugins/selection/plugin.js
index fd8332e..39c6db7 100644
--- a/_source/plugins/selection/plugin.js
+++ b/_source/plugins/selection/plugin.js
@@ -70,9 +70,37 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
// #### checkSelectionChange : END
+ function rangeRequiresFix( range )
+ {
+ function isInlineCt( node )
+ {
+ return node && node.type == CKEDITOR.NODE_ELEMENT
+ && 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;
+
+ if ( start.type == CKEDITOR.NODE_TEXT )
+ return false;
+
+ // 1. Empty inline element. ^
+ // 2. Adjoin to inline element.
text^
+ // 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 )
@@ -99,10 +127,116 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
canUndo : false
};
+ function createFillingChar( doc )
+ {
+ removeFillingChar( doc );
+
+ var fillingChar = doc.createText( '\u200B' );
+ doc.setCustomData( 'cke-fillingChar', fillingChar );
+
+ return fillingChar;
+ }
+
+ function getFillingChar( doc )
+ {
+ return doc && doc.getCustomData( 'cke-fillingChar' );
+ }
+
+ // Checks if a filling char has been used, eventualy removing it (#1272).
+ function checkFillingChar( doc )
+ {
+ var fillingChar = doc && getFillingChar( doc );
+ if ( fillingChar )
+ {
+ // Use this flag to avoid removing the filling char right after
+ // creating it.
+ if ( fillingChar.getCustomData( 'ready' ) )
+ removeFillingChar( doc );
+ else
+ fillingChar.setCustomData( 'ready', 1 );
+ }
+ }
+
+ function removeFillingChar( doc )
+ {
+ var fillingChar = doc && doc.removeCustomData( 'cke-fillingChar' );
+ if ( fillingChar )
+ {
+ // We can't simply remove the filling node because the user
+ // will actually enlarge it when typing, so we just remove the
+ // invisible char from it.
+ fillingChar.setText( fillingChar.getText().replace( /\u200B/g, '' ) );
+ fillingChar = 0;
+ }
+ }
+
CKEDITOR.plugins.add( 'selection',
{
init : function( editor )
{
+ // On WebKit only, we need a special "filling" char on some situations
+ // (#1272). Here we set the events that should invalidate that char.
+ if ( CKEDITOR.env.webkit )
+ {
+ editor.on( 'selectionChange', function() { checkFillingChar( editor.document ); } );
+ editor.on( 'beforeSetMode', function() { removeFillingChar( editor.document ); } );
+ editor.on( 'key', function( e )
+ {
+ // Remove the filling char before some keys get
+ // executed, so they'll not get blocked by it.
+ switch ( e.data.keyCode )
+ {
+ case 13 : // ENTER
+ case CKEDITOR.SHIFT + 13 : // SHIFT-ENTER
+ case 37 : // LEFT-ARROW
+ case 39 : // RIGHT-ARROW
+ case 8 : // BACKSPACE
+ removeFillingChar( editor.document );
+ }
+ }, null, null, 10 );
+
+ var fillingCharBefore,
+ resetSelection;
+
+ function beforeData()
+ {
+ var doc = editor.document,
+ fillingChar = getFillingChar( doc );
+
+ if ( fillingChar )
+ {
+ // If cursor is right blinking by side of the filler node, save it for restoring,
+ // as the following text substitution will blind it. (#7437)
+ var sel = doc.$.defaultView.getSelection();
+ if ( sel.type == 'Caret' && sel.anchorNode == fillingChar.$ )
+ resetSelection = 1;
+
+ fillingCharBefore = fillingChar.getText();
+ fillingChar.setText( fillingCharBefore.replace( /\u200B/g, '' ) );
+ }
+ }
+ function afterData()
+ {
+ var doc = editor.document,
+ fillingChar = getFillingChar( doc );
+
+ if ( fillingChar )
+ {
+ fillingChar.setText( fillingCharBefore );
+
+ if ( resetSelection )
+ {
+ doc.$.defaultView.getSelection().setPosition( fillingChar.$,fillingChar.getLength() );
+ resetSelection = 0;
+ }
+ }
+ }
+ editor.on( 'beforeUndoImage', beforeData );
+ editor.on( 'afterUndoImage', afterData );
+ editor.on( 'beforeGetData', beforeData, null, null, 0 );
+ editor.on( 'getData', afterData );
+ }
+
editor.on( 'contentDom', function()
{
var doc = editor.document,
@@ -134,10 +268,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
// point.
if ( savedRange )
{
- // 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 )
+ if ( restoreEnabled )
{
// Well not break because of this.
try
@@ -146,6 +277,14 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
}
catch (e)
{}
+
+ // Update locked selection because of the normalized text nodes. (#6083, #6987)
+ var lockedSelection = doc.getCustomData( 'cke_locked_selection' );
+ if ( lockedSelection )
+ {
+ lockedSelection.unlock();
+ lockedSelection.lock();
+ }
}
savedRange = null;
@@ -324,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 );
}
});
@@ -338,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()
@@ -363,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()
@@ -382,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;
@@ -402,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 )
{
@@ -420,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 )
{
@@ -437,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 ?
@@ -467,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:
*
- *
{@link CKEDITOR.SELECTION_NONE} (1): No selection.
- *
{@link CKEDITOR.SELECTION_TEXT} (2): Text is selected or
- * collapsed selection.
- *
{@link CKEDITOR.SELECTION_ELEMENT} (3): A element
- * selection.
+ *
{@link CKEDITOR.SELECTION_NONE} (1): No selection.
+ *
{@link CKEDITOR.SELECTION_TEXT} (2): A text or a collapsed
+ * selection is selected.
+ *
{@link CKEDITOR.SELECTION_ELEMENT} (3): An element is
+ * selected.
*
* @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 ?
@@ -547,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()
{
@@ -571,7 +722,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
range.collapse( start );
// Gets the element that encloses the range entirely.
- var parent = range.parentElement();
+ var parent = range.parentElement(),
+ doc = parent.ownerDocument;
// Empty parent element, e.g. ^
if ( !parent.hasChildNodes() )
@@ -579,6 +731,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
var siblings = parent.children,
child,
+ sibling,
testRange = range.duplicate(),
startIndex = 0,
endIndex = siblings.length - 1,
@@ -600,7 +753,21 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
else if ( position < 0 )
startIndex = index + 1;
else
- return { container : parent, offset : getNodeIndex( child ) };
+ {
+ // IE9 report wrong measurement with compareEndPoints when range anchors between two BRs.
+ // e.g.
text ^
(#7433)
+ if ( CKEDITOR.env.ie9Compat && child.tagName == 'BR' )
+ {
+ var bmId = 'cke_range_marker';
+ range.execCommand( 'CreateBookmark', false, bmId );
+ child = doc.getElementsByName( bmId )[ 0 ];
+ var offset = getNodeIndex( child );
+ parent.removeChild( child );
+ return { container : parent, offset : offset };
+ }
+ else
+ return { container : parent, offset : getNodeIndex( child ) };
+ }
}
// All childs are text nodes,
@@ -656,10 +823,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
// Start the measuring until distance overflows, meanwhile count the text nodes.
while ( distance > 0 )
{
- child = child[ position > 0 ? 'previousSibling' : 'nextSibling' ];
try
{
- distance -= child.nodeValue.length;
+ sibling = child[ position > 0 ? 'previousSibling' : 'nextSibling' ];
+ distance -= sibling.nodeValue.length;
+ child = sibling;
}
// Measurement in IE could be somtimes wrong because of