1 /* 2 * CKEditor - The text editor for Internet - http://ckeditor.com 3 * Copyright (C) 2003-2008 Frederico Caldeira Knabben 4 * 5 * == BEGIN LICENSE == 6 * 7 * Licensed under the terms of any of the following licenses at your 8 * choice: 9 * 10 * - GNU General Public License Version 2 or later (the "GPL") 11 * http://www.gnu.org/licenses/gpl.html 12 * 13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 14 * http://www.gnu.org/licenses/lgpl.html 15 * 16 * - Mozilla Public License Version 1.1 or later (the "MPL") 17 * http://www.mozilla.org/MPL/MPL-1.1.html 18 * 19 * == END LICENSE == 20 */ 21 22 (function() 23 { 24 // #### checkSelectionChange : START 25 26 // The selection change check basically saves the element parent tree of 27 // the current node and check it on successive requests. If there is any 28 // change on the tree, then the selectionChange event gets fired. 29 var checkSelectionPreviousPath; 30 var checkSelectionChange = function() 31 { 32 // In IE, the "selectionchange" event may still get thrown when 33 // releasing the WYSIWYG mode, so we need to check it first. 34 var sel = this.getSelection(); 35 if ( !sel ) 36 return; 37 38 // Get the element at the start of the selection. 39 var node = this.getSelection().getStartElement(), 40 changed, 41 currentPath = [], 42 counter = 0; 43 44 // Loops through the parent tree of the main node. 45 while( node ) 46 { 47 // Look for changes in the parent node tree. 48 if ( !changed && ( !checkSelectionPreviousPath || !node.equals( checkSelectionPreviousPath[ counter++ ] ) ) ) 49 changed = true; 50 51 currentPath.push( node ); 52 node = node.getParent(); 53 } 54 55 checkSelectionPreviousPath = currentPath; 56 57 if ( changed ) 58 this.fire( 'selectionChange' ); 59 }; 60 61 var checkSelectionChangeTimer; 62 var checkSelectionChangeTimeout = function() 63 { 64 if ( checkSelectionChangeTimer ) 65 clearTimeout( checkSelectionChangeTimer ); 66 67 checkSelectionChangeTimer = CKEDITOR.tools.setTimeout( checkSelectionChange, 100, this ); 68 }; 69 70 // #### checkSelectionChange : END 71 72 CKEDITOR.plugins.add( 'selection', 73 { 74 init : function( editor, pluginPath ) 75 { 76 editor.on( 'contentDom', function() 77 { 78 if ( CKEDITOR.env.ie ) 79 { 80 // IE is the only to provide the "selectionchange" 81 // event. 82 editor.document.on( 'selectionchange', checkSelectionChange, editor ); 83 } 84 else 85 { 86 // In other browsers, we make the selection change 87 // check based on other events, like clicks or keys 88 // press. 89 90 editor.document.on( 'mouseup', checkSelectionChange, editor ); 91 92 // Firing the "OnSelectionChange" event on every key 93 // press started to be too slow. So, a timer has been 94 // implemented to solve performance issues when typing 95 // to quickly. 96 editor.document.on( 'keyup', checkSelectionChangeTimeout, editor ); 97 } 98 }); 99 } 100 }); 101 })(); 102 103 /** 104 * Gets the current selection from the editing area when in WYSIWYG mode. 105 * @returns {CKEDITOR.dom.selection} A selection object or null if not on 106 * WYSIWYG mode or no selection is available. 107 * @example 108 * var selection = CKEDITOR.instances.editor1.<b>getSelection()</b>; 109 * alert( selection.getType() ); 110 */ 111 CKEDITOR.editor.prototype.getSelection = function() 112 { 113 return this.document ? this.document.getSelection() : null; 114 }; 115 116 /** 117 * Gets the current selection from the document. 118 * @returns {CKEDITOR.dom.selection} A selection object. 119 * @example 120 * var selection = CKEDITOR.instances.editor1.document.<b>getSelection()</b>; 121 * alert( selection.getType() ); 122 */ 123 CKEDITOR.dom.document.prototype.getSelection = function() 124 { 125 return new CKEDITOR.dom.selection( this ); 126 }; 127 128 /** 129 * No selection. 130 * @constant 131 * @example 132 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_NONE ) 133 * alert( 'Nothing is selected' ); 134 */ 135 CKEDITOR.SELECTION_NONE = 1; 136 137 /** 138 * Text or collapsed selection. 139 * @constant 140 * @example 141 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT ) 142 * alert( 'Text is selected' ); 143 */ 144 CKEDITOR.SELECTION_TEXT = 2; 145 146 /** 147 * Element selection. 148 * @constant 149 * @example 150 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_ELEMENT ) 151 * alert( 'An element is selected' ); 152 */ 153 CKEDITOR.SELECTION_ELEMENT = 3; 154 155 /** 156 * Manipulates the selection in a DOM document. 157 * @constructor 158 * @example 159 */ 160 CKEDITOR.dom.selection = function( document ) 161 { 162 this.document = document; 163 }; 164 165 (function() 166 { 167 var styleObjectElements = { img:1,hr:1,li:1,table:1,tr:1,td:1,embed:1,object:1,ol:1,ul:1 }; 168 169 CKEDITOR.dom.selection.prototype = 170 { 171 /** 172 * Gets the native selection object from the browser. 173 * @returns {Object} The native selection object. 174 * @example 175 * var selection = editor.getSelection().<b>getNative()</b>; 176 */ 177 getNative : (function() 178 { 179 if ( CKEDITOR.env.ie ) 180 return function() 181 { 182 return this.document.$.selection; 183 }; 184 else 185 return function() 186 { 187 return this.document.getWindow().$.getSelection(); 188 }; 189 })(), 190 191 /** 192 * Gets the type of the current selection. The following values are 193 * available: 194 * <ul> 195 * <li>{@link CKEDITOR.SELECTION_NONE} (1): No selection.</li> 196 * <li>{@link CKEDITOR.SELECTION_TEXT} (2): Text is selected or 197 * collapsed selection.</li> 198 * <li>{@link CKEDITOR.SELECTION_ELEMENT} (3): A element 199 * selection.</li> 200 * </ul> 201 * @returns {Number} One of the following constant values: 202 * {@link CKEDITOR.SELECTION_NONE}, {@link CKEDITOR.SELECTION_TEXT} or 203 * {@link CKEDITOR.SELECTION_ELEMENT}. 204 * @example 205 * if ( editor.getSelection().<b>getType()</b> == CKEDITOR.SELECTION_TEXT ) 206 * alert( 'Text is selected' ); 207 */ 208 getType : (function() 209 { 210 if ( CKEDITOR.env.ie ) 211 return function() 212 { 213 try 214 { 215 var sel = this.getNative(), 216 ieType = sel.type; 217 218 if ( ieType == 'Text' ) 219 return CKEDITOR.SELECTION_TEXT; 220 221 if ( ieType == 'Control' ) 222 return CKEDITOR.SELECTION_ELEMENT; 223 224 // It is possible that we can still get a text range 225 // object even when type == 'None' is returned by IE. 226 // So we'd better check the object returned by 227 // createRange() rather than by looking at the type. 228 if ( sel.createRange().parentElement ) 229 return CKEDITOR.SELECTION_TEXT; 230 } 231 catch(e) {} 232 233 return CKEDITOR.SELECTION_NONE; 234 }; 235 else 236 return function() 237 { 238 var sel = this.getNative(); 239 if ( !sel ) 240 return CKEDITOR.SELECTION_NONE; 241 242 if ( sel.rangeCount == 1 ) 243 { 244 // Check if the actual selection is a control (IMG, 245 // TABLE, HR, etc...). 246 247 var range = sel.getRangeAt(0), 248 startContainer = range.startContainer; 249 250 if ( startContainer == range.endContainer 251 && startContainer.nodeType == 1 252 && ( range.endOffset - range.startOffset ) == 1 253 && styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] ) 254 { 255 return CKEDITOR.SELECTION_ELEMENT; 256 } 257 } 258 259 return CKEDITOR.SELECTION_TEXT; 260 }; 261 })(), 262 263 /** 264 * Gets the DOM element in which the selection starts. 265 * @returns {CKEDITOR.dom.element} The element at the beginning of the 266 * selection. 267 * @example 268 * var element = editor.getSelection().<b>getStartElement()</b>; 269 * alert( element.getName() ); 270 */ 271 getStartElement : function() 272 { 273 var node, 274 sel = this.getNative(); 275 276 switch ( this.getType() ) 277 { 278 case CKEDITOR.SELECTION_ELEMENT : 279 return this.getSelectedElement(); 280 281 case CKEDITOR.SELECTION_TEXT : 282 283 if ( CKEDITOR.env.ie ) 284 { 285 var range = sel.createRange(); 286 range.collapse( true ); 287 288 node = range.parentElement(); 289 } 290 else 291 { 292 node = sel.anchorNode; 293 294 if ( node.nodeType != 1 ) 295 node = node.parentNode; 296 } 297 } 298 299 return ( node ? new CKEDITOR.dom.element( node ) : null ); 300 }, 301 302 /** 303 * Gets the current selected element. 304 * @returns {CKEDITOR.dom.element} The selected element. Null if no 305 * selection is available or the selection type is not 306 * {@link CKEDITOR.SELECTION_ELEMENT}. 307 * @example 308 * var element = editor.getSelection().<b>getSelectedElement()</b>; 309 * alert( element.getName() ); 310 */ 311 getSelectedElement : function() 312 { 313 var node; 314 315 if ( this.getType() == CKEDITOR.SELECTION_ELEMENT ) 316 { 317 var sel = this.getNative(); 318 319 if ( CKEDITOR.env.ie ) 320 { 321 try 322 { 323 node = sel.createRange().item(0); 324 } 325 catch(e) {} 326 } 327 else 328 { 329 var range = sel.getRangeAt( 0 ); 330 node = range.startContainer.childNodes[ range.startOffset ]; 331 } 332 } 333 334 return ( node ? new CKEDITOR.dom.element( node ) : null ); 335 } 336 }; 337 })();