/*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
\r
var selectAllCmd =\r
{\r
+ modes : { wysiwyg : 1, source : 1 },\r
exec : function( editor )\r
{\r
switch ( editor.mode )\r
editor.document.$.execCommand( 'SelectAll', false, null );\r
break;\r
case 'source' :\r
- // TODO\r
+ // Select the contents of the textarea\r
+ var textarea = editor.textarea.$ ;\r
+ if ( CKEDITOR.env.ie )\r
+ {\r
+ textarea.createTextRange().execCommand( 'SelectAll' ) ;\r
+ }\r
+ else\r
+ {\r
+ textarea.selectionStart = 0 ;\r
+ textarea.selectionEnd = textarea.value.length ;\r
+ }\r
+ textarea.focus() ;\r
}\r
},\r
canUndo : false\r
{\r
editor.on( 'contentDom', function()\r
{\r
- var doc = editor.document;\r
+ var doc = editor.document,\r
+ body = doc.getBody();\r
\r
if ( CKEDITOR.env.ie )\r
{\r
// "onfocusin" is fired before "onfocus". It makes it\r
// possible to restore the selection before click\r
// events get executed.\r
- doc.on( 'focusin', function()\r
+ body.on( 'focusin', function( evt )\r
{\r
+ // If there are elements with layout they fire this event but\r
+ // it must be ignored to allow edit its contents #4682\r
+ if ( evt.data.$.srcElement.nodeName != 'BODY' )\r
+ return;\r
+\r
// If we have saved a range, restore it at this\r
// point.\r
if ( savedRange )\r
}\r
});\r
\r
- editor.window.on( 'focus', function()\r
+ body.on( 'focus', function()\r
{\r
// Enable selections to be saved.\r
saveEnabled = true;\r
saveSelection();\r
});\r
\r
- // Check document selection before 'blur' fired, this\r
- // will prevent us from breaking text selection somewhere\r
- // else on the host page.(#3909)\r
- editor.document.on( 'beforedeactivate', function()\r
+ body.on( 'beforedeactivate', function( evt )\r
{\r
+ // Ignore this event if it's caused by focus switch between\r
+ // internal editable control type elements, e.g. layouted paragraph. (#4682)\r
+ if ( evt.data.$.toElement )\r
+ return;\r
+\r
// Disable selections from being saved.\r
saveEnabled = false;\r
+ });\r
\r
- // IE may leave the selection still inside the\r
- // document. Let's force it to be removed.\r
- // TODO: The following has effect for\r
- // collapsed selections.\r
- editor.document.$.execCommand( 'Unselect' );\r
+ // IE before version 8 will leave cursor blinking inside the document after\r
+ // editor blurred unless we clean up the selection. (#4716)\r
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )\r
+ {\r
+ doc.getWindow().on( 'blur', function( evt )\r
+ {\r
+ editor.document.$.selection.empty();\r
});\r
+ }\r
\r
// IE fires the "selectionchange" event when clicking\r
// inside a selection. We don't want to capture that.\r
- doc.on( 'mousedown', disableSave );\r
- doc.on( 'mouseup',\r
+ body.on( 'mousedown', disableSave );\r
+ body.on( 'mouseup',\r
function()\r
{\r
saveEnabled = true;\r
0 );\r
});\r
\r
- doc.on( 'keydown', disableSave );\r
- doc.on( 'keyup',\r
+ body.on( 'keydown', disableSave );\r
+ body.on( 'keyup',\r
function()\r
{\r
saveEnabled = true;\r
if ( saveEnabled )\r
{\r
var doc = editor.document,\r
- sel = doc && doc.$.selection;\r
+ sel = editor.getSelection(),\r
+ nativeSel = sel && sel.getNative();\r
\r
// There is a very specific case, when clicking\r
// inside a text selection. In that case, the\r
// range at the very start of the document. In\r
// such situation we have to test the range, to\r
// be sure it's valid.\r
- if ( testIt && sel && sel.type == 'None' )\r
+ if ( testIt && nativeSel && nativeSel.type == 'None' )\r
{\r
// The "InsertImage" command can be used to\r
// test whether the selection is good or not.\r
}\r
}\r
\r
- savedRange = sel && sel.createRange();\r
+ // Avoid saving selection from within text input. (#5747)\r
+ var parentTag;\r
+ if ( nativeSel && nativeSel.type == 'Text'\r
+ && ( parentTag = nativeSel.createRange().parentElement().nodeName.toLowerCase() )\r
+ && parentTag in { input: 1, textarea : 1 } )\r
+ {\r
+ return;\r
+ }\r
+\r
+ savedRange = nativeSel && sel.getRanges()[ 0 ];\r
\r
checkSelectionChangeTimeout.call( editor );\r
}\r
\r
var styleObjectElements =\r
{\r
- img:1,hr:1,li:1,table:1,tr:1,td:1,embed:1,object:1,ol:1,ul:1,\r
+ img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,\r
a:1, input:1, form:1, select:1, textarea:1, button:1, fieldset:1, th:1, thead:1, tfoot:1\r
};\r
\r
testRange = range.duplicate();\r
\r
testRange.moveToElementText( child );\r
- testRange.collapse();\r
\r
- var comparison = testRange.compareEndPoints( 'StartToStart', range );\r
+ var comparisonStart = testRange.compareEndPoints( 'StartToStart', range ),\r
+ comparisonEnd = testRange.compareEndPoints( 'EndToStart', range );\r
\r
- if ( comparison > 0 )\r
+ testRange.collapse();\r
+\r
+ if ( comparisonStart > 0 )\r
break;\r
- else if ( comparison === 0 )\r
- return {\r
- container : parent,\r
- offset : i\r
- };\r
+ // When selection stay at the side of certain self-closing elements, e.g. BR,\r
+ // our comparison will never shows an equality. (#4824)\r
+ else if ( !comparisonStart\r
+ || comparisonEnd == 1 && comparisonStart == -1 )\r
+ return { container : parent, offset : i };\r
+ else if ( !comparisonEnd )\r
+ return { container : parent, offset : i + 1 };\r
\r
testRange = null;\r
}\r
// breaking character counting logic below. (#3949)\r
var distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;\r
\r
- while ( distance > 0 )\r
- distance -= siblings[ --i ].nodeValue.length;\r
+ try\r
+ {\r
+ while ( distance > 0 )\r
+ distance -= siblings[ --i ].nodeValue.length;\r
+ }\r
+ // Measurement in IE could be somtimes wrong because of <select> element. (#4611)\r
+ catch( e )\r
+ {\r
+ distance = 0;\r
+ }\r
+\r
\r
if ( distance === 0 )\r
{\r
// Decrease the range content to exclude particial\r
// selected node on the start which doesn't have\r
// visual impact. ( #3231 )\r
- while( true )\r
+ while ( true )\r
{\r
var startContainer = range.startContainer,\r
startOffset = range.startOffset;\r
+ // Limit the fix only to non-block elements.(#3950)\r
if ( startOffset == ( startContainer.getChildCount ?\r
- startContainer.getChildCount() : startContainer.getLength() ) )\r
+ startContainer.getChildCount() : startContainer.getLength() )\r
+ && !startContainer.isBlockBoundary() )\r
range.setStartAfter( startContainer );\r
else break;\r
}\r
{\r
node = sel.anchorNode;\r
\r
- if ( node.nodeType != 1 )\r
+ if ( node && node.nodeType != 1 )\r
node = node.parentNode;\r
}\r
}\r
if ( cache.selectedElement !== undefined )\r
return cache.selectedElement;\r
\r
- var node;\r
-\r
- if ( this.getType() == CKEDITOR.SELECTION_ELEMENT )\r
- {\r
- var sel = this.getNative();\r
+ var self = this;\r
\r
- if ( CKEDITOR.env.ie )\r
+ var node = CKEDITOR.tools.tryThese(\r
+ // Is it native IE control type selection?\r
+ function()\r
{\r
- try\r
+ return self.getNative().createRange().item( 0 );\r
+ },\r
+ // Figure it out by checking if there's a single enclosed\r
+ // node of the range.\r
+ function()\r
+ {\r
+ var range = self.getRanges()[ 0 ],\r
+ enclosed,\r
+ selected;\r
+\r
+ // Check first any enclosed element, e.g. <ul>[<li><a href="#">item</a></li>]</ul>\r
+ for ( var i = 2; i && !( ( enclosed = range.getEnclosedNode() )\r
+ && ( enclosed.type == CKEDITOR.NODE_ELEMENT )\r
+ && styleObjectElements[ enclosed.getName() ]\r
+ && ( selected = enclosed ) ); i-- )\r
{\r
- node = sel.createRange().item(0);\r
+ // Then check any deep wrapped element, e.g. [<b><i><img /></i></b>]\r
+ range.shrink( CKEDITOR.SHRINK_ELEMENT );\r
}\r
- catch(e) {}\r
- }\r
- else\r
- {\r
- var range = sel.getRangeAt( 0 );\r
- node = range.startContainer.childNodes[ range.startOffset ];\r
- }\r
- }\r
+\r
+ return selected.$;\r
+ });\r
\r
return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null );\r
},\r
range.moveToElementText( element.$ );\r
range.select();\r
}\r
+ finally\r
+ {\r
+ this.document.fire( 'selectionchange' );\r
+ }\r
\r
this.reset();\r
}\r
}\r
this.selectRanges( ranges );\r
return this;\r
+ },\r
+\r
+ getCommonAncestor : function()\r
+ {\r
+ var ranges = this.getRanges(),\r
+ startNode = ranges[ 0 ].startContainer,\r
+ endNode = ranges[ ranges.length - 1 ].endContainer;\r
+ return startNode.getCommonAncestor( endNode );\r
+ },\r
+\r
+ // Moving scroll bar to the current selection's start position.\r
+ scrollIntoView : function()\r
+ {\r
+ // If we have split the block, adds a temporary span at the\r
+ // range position and scroll relatively to it.\r
+ var start = this.getStartElement();\r
+ start.scrollIntoView();\r
}\r
};\r
})();\r
+( function()\r
+{\r
+var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true );\r
+var fillerTextRegex = /\ufeff|\u00a0/;\r
+var nonCells = { table:1,tbody:1,tr:1 };\r
\r
CKEDITOR.dom.range.prototype.select =\r
CKEDITOR.env.ie ?\r
var isStartMarkerAlone;\r
var dummySpan;\r
\r
+ // IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g.\r
+ // <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>...\r
+ if ( this.startContainer.type == CKEDITOR.NODE_ELEMENT && this.startContainer.getName() in nonCells\r
+ || this.endContainer.type == CKEDITOR.NODE_ELEMENT && this.endContainer.getName() in nonCells )\r
+ {\r
+ this.shrink( CKEDITOR.NODE_ELEMENT, true );\r
+ }\r
+\r
var bookmark = this.createBookmark();\r
\r
// Create marker tags for the start and end boundaries.\r
// will expand and that the cursor will be blinking on the right place.\r
// Actually, we are using this flag just to avoid using this hack in all\r
// situations, but just on those needed.\r
- isStartMarkerAlone = forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) );\r
+ var next = startNode.getNext( notWhitespaces );\r
+ isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) ) // already a filler there?\r
+ && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) );\r
\r
// Append a temporary <span></span> before the selection.\r
// This is needed to avoid IE destroying selections inside empty\r
else\r
ieRange.select();\r
\r
+ this.moveToPosition( dummySpan, CKEDITOR.POSITION_BEFORE_START );\r
dummySpan.remove();\r
}\r
else\r
endNode.remove();\r
ieRange.select();\r
}\r
+\r
+ this.document.fire( 'selectionchange' );\r
}\r
:\r
function()\r
selection.removeAllRanges();\r
selection.addRange( nativeRange );\r
};\r
+} )();\r