1 /*
  2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5 
  6 (function()
  7 {
  8 	// #### checkSelectionChange : START
  9 
 10 	// The selection change check basically saves the element parent tree of
 11 	// the current node and check it on successive requests. If there is any
 12 	// change on the tree, then the selectionChange event gets fired.
 13 	function checkSelectionChange()
 14 	{
 15 		try
 16 		{
 17 			// In IE, the "selectionchange" event may still get thrown when
 18 			// releasing the WYSIWYG mode, so we need to check it first.
 19 			var sel = this.getSelection();
 20 			if ( !sel || !sel.document.getWindow().$ )
 21 				return;
 22 
 23 			var firstElement = sel.getStartElement();
 24 			var currentPath = new CKEDITOR.dom.elementPath( firstElement );
 25 
 26 			if ( !currentPath.compare( this._.selectionPreviousPath ) )
 27 			{
 28 				this._.selectionPreviousPath = currentPath;
 29 				this.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } );
 30 			}
 31 		}
 32 		catch (e)
 33 		{}
 34 	}
 35 
 36 	var checkSelectionChangeTimer,
 37 		checkSelectionChangeTimeoutPending;
 38 
 39 	function checkSelectionChangeTimeout()
 40 	{
 41 		// Firing the "OnSelectionChange" event on every key press started to
 42 		// be too slow. This function guarantees that there will be at least
 43 		// 200ms delay between selection checks.
 44 
 45 		checkSelectionChangeTimeoutPending = true;
 46 
 47 		if ( checkSelectionChangeTimer )
 48 			return;
 49 
 50 		checkSelectionChangeTimeoutExec.call( this );
 51 
 52 		checkSelectionChangeTimer = CKEDITOR.tools.setTimeout( checkSelectionChangeTimeoutExec, 200, this );
 53 	}
 54 
 55 	function checkSelectionChangeTimeoutExec()
 56 	{
 57 		checkSelectionChangeTimer = null;
 58 
 59 		if ( checkSelectionChangeTimeoutPending )
 60 		{
 61 			// Call this with a timeout so the browser properly moves the
 62 			// selection after the mouseup. It happened that the selection was
 63 			// being moved after the mouseup when clicking inside selected text
 64 			// with Firefox.
 65 			CKEDITOR.tools.setTimeout( checkSelectionChange, 0, this );
 66 
 67 			checkSelectionChangeTimeoutPending = false;
 68 		}
 69 	}
 70 
 71 	// #### checkSelectionChange : END
 72 
 73 	function rangeRequiresFix( range )
 74 	{
 75 		function isInlineCt( node )
 76 		{
 77 			return node && node.type == CKEDITOR.NODE_ELEMENT
 78 					&& node.getName() in CKEDITOR.dtd.$removeEmpty;
 79 		}
 80 
 81 		function singletonBlock( node )
 82 		{
 83 			var body = range.document.getBody();
 84 			return !node.is( 'body' ) && body.getChildCount() == 1;
 85 		}
 86 
 87 		var start = range.startContainer,
 88 			offset = range.startOffset;
 89 
 90 		if ( start.type == CKEDITOR.NODE_TEXT )
 91 			return false;
 92 
 93 		// 1. Empty inline element. <span>^</span>
 94 		// 2. Adjoin to inline element. <p><strong>text</strong>^</p>
 95 		// 3. The only empty block in document. <body><p>^</p></body> (#7222)
 96 		return !CKEDITOR.tools.trim( start.getHtml() ) ? isInlineCt( start ) || singletonBlock( start )
 97 				: isInlineCt( start.getChild( offset - 1 ) ) || isInlineCt( start.getChild( offset ) );
 98 	}
 99 
100 	var selectAllCmd =
101 	{
102 		modes : { wysiwyg : 1, source : 1 },
103 		readOnly : CKEDITOR.env.ie || CKEDITOR.env.webkit,
104 		exec : function( editor )
105 		{
106 			switch ( editor.mode )
107 			{
108 				case 'wysiwyg' :
109 					editor.document.$.execCommand( 'SelectAll', false, null );
110 					// Force triggering selectionChange (#7008)
111 					editor.forceNextSelectionCheck();
112 					editor.selectionChange();
113 					break;
114 				case 'source' :
115 					// Select the contents of the textarea
116 					var textarea = editor.textarea.$;
117 					if ( CKEDITOR.env.ie )
118 						textarea.createTextRange().execCommand( 'SelectAll' );
119 					else
120 					{
121 						textarea.selectionStart = 0;
122 						textarea.selectionEnd = textarea.value.length;
123 					}
124 					textarea.focus();
125 			}
126 		},
127 		canUndo : false
128 	};
129 
130 	function createFillingChar( doc )
131 	{
132 		removeFillingChar( doc );
133 
134 		var fillingChar = doc.createText( '\u200B' );
135 		doc.setCustomData( 'cke-fillingChar', fillingChar );
136 
137 		return fillingChar;
138 	}
139 
140 	function getFillingChar( doc )
141 	{
142 		return doc && doc.getCustomData( 'cke-fillingChar' );
143 	}
144 
145 	// Checks if a filling char has been used, eventualy removing it (#1272).
146 	function checkFillingChar( doc )
147 	{
148 		var fillingChar = doc && getFillingChar( doc );
149 		if ( fillingChar )
150 		{
151 			// Use this flag to avoid removing the filling char right after
152 			// creating it.
153 			if ( fillingChar.getCustomData( 'ready' ) )
154 				removeFillingChar( doc );
155 			else
156 				fillingChar.setCustomData( 'ready', 1 );
157 		}
158 	}
159 
160 	function removeFillingChar( doc )
161 	{
162 		var fillingChar = doc && doc.removeCustomData( 'cke-fillingChar' );
163 		if ( fillingChar )
164 		{
165 			var bm,
166 			sel = doc.getSelection().getNative(),
167 			// Be error proof.
168 			range = sel && sel.type != 'None' && sel.getRangeAt( 0 );
169 
170 			// Text selection position might get mangled by
171 			// subsequent dom modification, save it now for restoring. (#8617)
172 			if ( fillingChar.getLength() > 1
173 				 && range && range.intersectsNode( fillingChar.$ ) )
174 			{
175 				bm = [ sel.anchorOffset, sel.focusOffset ];
176 
177 				// Anticipate the offset change brought by the removed char.
178 				var startAffected = sel.anchorNode == fillingChar.$ && sel.anchorOffset > 0,
179 					endAffected = sel.focusNode == fillingChar.$ && sel.focusOffset > 0;
180 				startAffected && bm[ 0 ]--;
181 				endAffected && bm[ 1 ]--;
182 
183 				// Revert the bookmark order on reverse selection.
184 				isReversedSelection( sel ) && bm.unshift( bm.pop() );
185 			}
186 
187 			// We can't simply remove the filling node because the user
188 			// will actually enlarge it when typing, so we just remove the
189 			// invisible char from it.
190 			fillingChar.setText( fillingChar.getText().replace( /\u200B/g, '' ) );
191 
192 			// Restore the bookmark.
193 			if ( bm )
194 			{
195 				var rng = sel.getRangeAt( 0 );
196 				rng.setStart( rng.startContainer, bm[ 0 ] );
197 				rng.setEnd( rng.startContainer, bm[ 1 ] );
198 				sel.removeAllRanges();
199 				sel.addRange( rng );
200 			}
201 		}
202 	}
203 
204 	function isReversedSelection( sel )
205 	{
206 		if ( !sel.isCollapsed )
207 		{
208 			var range = sel.getRangeAt( 0 );
209 			// Potentially alter an reversed selection range.
210 			range.setStart( sel.anchorNode, sel.anchorOffset );
211 			range.setEnd( sel.focusNode, sel.focusOffset );
212 			return range.collapsed;
213 		}
214 	}
215 
216 	CKEDITOR.plugins.add( 'selection',
217 	{
218 		init : function( editor )
219 		{
220 			// On WebKit only, we need a special "filling" char on some situations
221 			// (#1272). Here we set the events that should invalidate that char.
222 			if ( CKEDITOR.env.webkit )
223 			{
224 				editor.on( 'selectionChange', function() { checkFillingChar( editor.document ); } );
225 				editor.on( 'beforeSetMode', function() { removeFillingChar( editor.document ); } );
226 
227 				var fillingCharBefore,
228 					resetSelection;
229 
230 				function beforeData()
231 				{
232 					var doc = editor.document,
233 						fillingChar = getFillingChar( doc );
234 
235 					if ( fillingChar )
236 					{
237 						// If cursor is right blinking by side of the filler node, save it for restoring,
238 						// as the following text substitution will blind it. (#7437)
239 						var sel = doc.$.defaultView.getSelection();
240 						if ( sel.type == 'Caret' && sel.anchorNode == fillingChar.$ )
241 							resetSelection = 1;
242 
243 						fillingCharBefore = fillingChar.getText();
244 						fillingChar.setText( fillingCharBefore.replace( /\u200B/g, '' ) );
245 					}
246 				}
247 				function afterData()
248 				{
249 					var doc = editor.document,
250 						fillingChar = getFillingChar( doc );
251 
252 					if ( fillingChar )
253 					{
254 						fillingChar.setText( fillingCharBefore );
255 
256 						if ( resetSelection )
257 						{
258 							doc.$.defaultView.getSelection().setPosition( fillingChar.$,fillingChar.getLength() );
259 							resetSelection = 0;
260 						}
261 					}
262 				}
263 				editor.on( 'beforeUndoImage', beforeData );
264 				editor.on( 'afterUndoImage', afterData );
265 				editor.on( 'beforeGetData', beforeData, null, null, 0 );
266 				editor.on( 'getData', afterData );
267 			}
268 
269 			editor.on( 'contentDom', function()
270 				{
271 					var doc = editor.document,
272 						body = doc.getBody(),
273 						html = doc.getDocumentElement();
274 
275 					if ( CKEDITOR.env.ie )
276 					{
277 						// Other browsers don't loose the selection if the
278 						// editor document loose the focus. In IE, we don't
279 						// have support for it, so we reproduce it here, other
280 						// than firing the selection change event.
281 
282 						var savedRange,
283 							saveEnabled,
284 							restoreEnabled = 1;
285 
286 						// "onfocusin" is fired before "onfocus". It makes it
287 						// possible to restore the selection before click
288 						// events get executed.
289 						body.on( 'focusin', function( evt )
290 							{
291 								// If there are elements with layout they fire this event but
292 								// it must be ignored to allow edit its contents #4682
293 								if ( evt.data.$.srcElement.nodeName != 'BODY' )
294 									return;
295 
296 								// Give the priority to locked selection since it probably
297 								// reflects the actual situation, besides locked selection
298 								// could be interfered because of text nodes normalizing.
299 								// (#6083, #6987)
300 								var lockedSelection = doc.getCustomData( 'cke_locked_selection' );
301 								if ( lockedSelection )
302 								{
303 									lockedSelection.unlock( 1 );
304 									lockedSelection.lock();
305 								}
306 								// Then check ff we have saved a range, restore it at this
307 								// point.
308 								else if ( savedRange && restoreEnabled )
309 								{
310 									// Well not break because of this.
311 									try { savedRange.select(); } catch (e) {}
312 									savedRange = null;
313 								}
314 							});
315 
316 						body.on( 'focus', function()
317 							{
318 								// Enable selections to be saved.
319 								saveEnabled = 1;
320 
321 								saveSelection();
322 							});
323 
324 						body.on( 'beforedeactivate', function( evt )
325 							{
326 								// Ignore this event if it's caused by focus switch between
327 								// internal editable control type elements, e.g. layouted paragraph. (#4682)
328 								if ( evt.data.$.toElement )
329 									return;
330 
331 								// Disable selections from being saved.
332 								saveEnabled = 0;
333 								restoreEnabled = 1;
334 							});
335 
336 						// [IE] Iframe will still keep the selection when blurred, if
337 						// focus is moved onto a non-editing host, e.g. link or button, but
338 						// it becomes a problem for the object type selection, since the resizer
339 						// handler attached on it will mark other part of the UI, especially
340 						// for the dialog. (#8157)
341 						// [IE<8] Even worse For old IEs, the cursor will not vanish even if
342 						// the selection has been moved to another text input in some cases. (#4716)
343 						//
344 						// Now the range restore is disabled, so we simply force IE to clean
345 						// up the selection before blur.
346 						CKEDITOR.env.ie && editor.on( 'blur', function()
347 						{
348 							// Error proof when the editor is not visible. (#6375)
349 							try{ doc.$.selection.empty(); } catch ( er){}
350 						});
351 
352 						// Listening on document element ensures that
353 						// scrollbar is included. (#5280)
354 						html.on( 'mousedown', function()
355 						{
356 							// Lock restore selection now, as we have
357 							// a followed 'click' event which introduce
358 							// new selection. (#5735)
359 							restoreEnabled = 0;
360 						});
361 
362 						html.on( 'mouseup', function()
363 						{
364 							restoreEnabled = 1;
365 						});
366 
367 						var scroll;
368 						// IE fires the "selectionchange" event when clicking
369 						// inside a selection. We don't want to capture that.
370 						body.on( 'mousedown', function( evt )
371 						{
372 							// IE scrolls document to top on right mousedown
373 							// when editor has no focus, remember this scroll
374 							// position and revert it before context menu opens. (#5778)
375 							if ( evt.data.$.button == 2 )
376 							{
377 								var sel = editor.document.$.selection;
378 								if ( sel.type == 'None' )
379 									scroll = editor.window.getScrollPosition();
380 							}
381 							disableSave();
382 						});
383 
384 						body.on( 'mouseup',
385 							function( evt )
386 							{
387 								// Restore recorded scroll position when needed on right mouseup.
388 								if ( evt.data.$.button == 2 && scroll )
389 								{
390 									editor.document.$.documentElement.scrollLeft = scroll.x;
391 									editor.document.$.documentElement.scrollTop = scroll.y;
392 								}
393 								scroll = null;
394 
395 								saveEnabled = 1;
396 								setTimeout( function()
397 									{
398 										saveSelection( true );
399 									},
400 									0 );
401 							});
402 
403 						body.on( 'keydown', disableSave );
404 						body.on( 'keyup',
405 							function()
406 							{
407 								saveEnabled = 1;
408 								saveSelection();
409 							});
410 
411 						// When content doc is in standards mode, IE doesn't focus the editor when
412 						// clicking at the region below body (on html element) content, we emulate
413 						// the normal behavior on old IEs. (#1659, #7932)
414 						if ( ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat )
415 							 && doc.$.compatMode != 'BackCompat' )
416 						{
417 							html.on( 'mousedown', function( evt )
418 							{
419 								evt = evt.data.$;
420 
421 								// Expand the text range along with mouse move.
422 								function onHover( evt )
423 								{
424 									evt = evt.data.$;
425 									if ( textRng )
426 									{
427 										// Read the current cursor.
428 										var rngEnd = body.$.createTextRange();
429 										rngEnd.moveToPoint( evt.x, evt.y );
430 
431 										// Handle drag directions.
432 										textRng.setEndPoint(
433 											textRng.compareEndPoints( 'StartToStart', rngEnd ) < 0 ?
434 											'EndToEnd' :
435 											'StartToStart',
436 											rngEnd );
437 
438 										// Update selection with new range.
439 										textRng.select();
440 									}
441 								}
442 
443 								// We're sure that the click happens at the region
444 								// below body, but not on scrollbar.
445 								if ( evt.y < html.$.clientHeight
446 									 && evt.y > body.$.offsetTop + body.$.clientHeight
447 									 && evt.x < html.$.clientWidth )
448 								{
449 									// Start to build the text range.
450 									var textRng = body.$.createTextRange();
451 									textRng.moveToPoint( evt.x, evt.y );
452 									textRng.select();
453 
454 									html.on( 'mousemove', onHover );
455 
456 									html.on( 'mouseup', function( evt )
457 									{
458 										html.removeListener( 'mousemove', onHover );
459 										evt.removeListener();
460 										textRng.select();
461 										textRng = null;
462 									} );
463 								}
464 							});
465 						}
466 
467 						// It's much simpler for IE8, we just need to reselect the reported range.
468 						if ( CKEDITOR.env.ie8 )
469 						{
470 							html.on( 'mouseup', function( evt )
471 							{
472 								// The event is not fired when clicking on the scrollbars,
473 								// so we can safely check the following to understand
474 								// whether the empty space following <body> has been clicked.
475 								if ( evt.data.getTarget().getName() == 'html' )
476 								{
477 									var sel = CKEDITOR.document.$.selection,
478 										range = sel.createRange();
479 									// The selection range is reported on host, but actually it should applies to the content doc.
480 									if ( sel.type != 'None' && range.parentElement().ownerDocument == doc.$ )
481 										range.select();
482 								}
483 							} );
484 						}
485 
486 						// IE is the only to provide the "selectionchange"
487 						// event.
488 						doc.on( 'selectionchange', saveSelection );
489 
490 						function disableSave()
491 						{
492 							saveEnabled = 0;
493 						}
494 
495 						function saveSelection( testIt )
496 						{
497 							if ( saveEnabled )
498 							{
499 								var doc = editor.document,
500 									sel = editor.getSelection(),
501 									nativeSel = sel && sel.getNative();
502 
503 								// There is a very specific case, when clicking
504 								// inside a text selection. In that case, the
505 								// selection collapses at the clicking point,
506 								// but the selection object remains in an
507 								// unknown state, making createRange return a
508 								// range at the very start of the document. In
509 								// such situation we have to test the range, to
510 								// be sure it's valid.
511 								if ( testIt && nativeSel && nativeSel.type == 'None' )
512 								{
513 									// The "InsertImage" command can be used to
514 									// test whether the selection is good or not.
515 									// If not, it's enough to give some time to
516 									// IE to put things in order for us.
517 									if ( !doc.$.queryCommandEnabled( 'InsertImage' ) )
518 									{
519 										CKEDITOR.tools.setTimeout( saveSelection, 50, this, true );
520 										return;
521 									}
522 								}
523 
524 								// Avoid saving selection from within text input. (#5747)
525 								var parentTag;
526 								if ( nativeSel && nativeSel.type && nativeSel.type != 'Control'
527 									&& ( parentTag = nativeSel.createRange() )
528 									&& ( parentTag = parentTag.parentElement() )
529 									&& ( parentTag = parentTag.nodeName )
530 									&& parentTag.toLowerCase() in { input: 1, textarea : 1 } )
531 								{
532 									return;
533 								}
534 
535 								savedRange = nativeSel && sel.getRanges()[ 0 ];
536 
537 								checkSelectionChangeTimeout.call( editor );
538 							}
539 						}
540 					}
541 					else
542 					{
543 						// In other browsers, we make the selection change
544 						// check based on other events, like clicks or keys
545 						// press.
546 
547 						doc.on( 'mouseup', checkSelectionChangeTimeout, editor );
548 						doc.on( 'keyup', checkSelectionChangeTimeout, editor );
549 						doc.on( 'selectionchange', checkSelectionChangeTimeout, editor );
550 					}
551 
552 					if ( CKEDITOR.env.webkit )
553 					{
554 						doc.on( 'keydown', function( evt )
555 						{
556 							var key = evt.data.getKey();
557 							// Remove the filling char before some keys get
558 							// executed, so they'll not get blocked by it.
559 							switch ( key )
560 							{
561 								case 13 :	// ENTER
562 								case 33 :	// PAGEUP
563 								case 34 :	// PAGEDOWN
564 								case 35 :	// HOME
565 								case 36 :	// END
566 								case 37 :	// LEFT-ARROW
567 								case 39 :	// RIGHT-ARROW
568 								case 8 :	// BACKSPACE
569 								case 45 :	// INS
570 								case 46 :	// DEl
571 									removeFillingChar( editor.document );
572 							}
573 
574 						}, null, null, 10 );
575 					}
576 				});
577 
578 			// Clear the cached range path before unload. (#7174)
579 			editor.on( 'contentDomUnload', editor.forceNextSelectionCheck, editor );
580 
581 			editor.addCommand( 'selectAll', selectAllCmd );
582 			editor.ui.addButton( 'SelectAll',
583 				{
584 					label : editor.lang.selectAll,
585 					command : 'selectAll'
586 				});
587 
588 			editor.selectionChange = checkSelectionChangeTimeout;
589 
590 			// IE9 might cease to work if there's an object selection inside the iframe (#7639).
591 			CKEDITOR.env.ie9Compat && editor.on( 'destroy', function()
592 			{
593 				var sel = editor.getSelection();
594 				sel && sel.getNative().clear();
595 			}, null, null, 9 );
596 		}
597 	});
598 
599 	/**
600 	 * Gets the current selection from the editing area when in WYSIWYG mode.
601 	 * @returns {CKEDITOR.dom.selection} A selection object or null if not in
602 	 *		WYSIWYG mode or no selection is available.
603 	 * @example
604 	 * var selection = CKEDITOR.instances.editor1.<strong>getSelection()</strong>;
605 	 * alert( selection.getType() );
606 	 */
607 	CKEDITOR.editor.prototype.getSelection = function()
608 	{
609 		return this.document && this.document.getSelection();
610 	};
611 
612 	CKEDITOR.editor.prototype.forceNextSelectionCheck = function()
613 	{
614 		delete this._.selectionPreviousPath;
615 	};
616 
617 	/**
618 	 * Gets the current selection from the document.
619 	 * @returns {CKEDITOR.dom.selection} A selection object.
620 	 * @example
621 	 * var selection = CKEDITOR.instances.editor1.document.<strong>getSelection()</strong>;
622 	 * alert( selection.getType() );
623 	 */
624 	CKEDITOR.dom.document.prototype.getSelection = function()
625 	{
626 		var sel = new CKEDITOR.dom.selection( this );
627 		return ( !sel || sel.isInvalid ) ? null : sel;
628 	};
629 
630 	/**
631 	 * No selection.
632 	 * @constant
633 	 * @example
634 	 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_NONE )
635 	 *     alert( 'Nothing is selected' );
636 	 */
637 	CKEDITOR.SELECTION_NONE		= 1;
638 
639 	/**
640 	 * A text or a collapsed selection.
641 	 * @constant
642 	 * @example
643 	 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT )
644 	 *     alert( 'A text is selected' );
645 	 */
646 	CKEDITOR.SELECTION_TEXT		= 2;
647 
648 	/**
649 	 * Element selection.
650 	 * @constant
651 	 * @example
652 	 * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_ELEMENT )
653 	 *     alert( 'An element is selected' );
654 	 */
655 	CKEDITOR.SELECTION_ELEMENT	= 3;
656 
657 	/**
658 	 * Manipulates the selection in a DOM document.
659 	 * @constructor
660 	 * @param {CKEDITOR.dom.document} document The DOM document that contains the selection.
661 	 * @example
662 	 * var sel = new <strong>CKEDITOR.dom.selection( CKEDITOR.document )</strong>;
663 	 */
664 	CKEDITOR.dom.selection = function( document )
665 	{
666 		var lockedSelection = document.getCustomData( 'cke_locked_selection' );
667 
668 		if ( lockedSelection )
669 			return lockedSelection;
670 
671 		this.document = document;
672 		this.isLocked = 0;
673 		this._ =
674 		{
675 			cache : {}
676 		};
677 
678 		/**
679 		 * IE BUG: The selection's document may be a different document than the
680 		 * editor document. Return null if that is the case.
681 		 */
682 		if ( CKEDITOR.env.ie )
683 		{
684 			// Avoid breaking because of it. (#8836)
685 			try
686 			{
687 				var range = this.getNative().createRange();
688 				if ( !range ||
689 					 ( range.item && range.item( 0 ).ownerDocument != this.document.$ ) ||
690 					 ( range.parentElement && range.parentElement().ownerDocument != this.document.$ ) )
691 				{
692 					throw 0;
693 				}
694 			}
695 			catch ( e )
696 			{
697 				this.isInvalid = true;
698 			}
699 		}
700 
701 		return this;
702 	};
703 
704 	var styleObjectElements =
705 		{
706 			img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,
707 			a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1
708 		};
709 
710 	CKEDITOR.dom.selection.prototype =
711 	{
712 		/**
713 		 * Gets the native selection object from the browser.
714 		 * @function
715 		 * @returns {Object} The native browser selection object.
716 		 * @example
717 		 * var selection = editor.getSelection().<strong>getNative()</strong>;
718 		 */
719 		getNative :
720 			CKEDITOR.env.ie ?
721 				function()
722 				{
723 					return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.$.selection );
724 				}
725 			:
726 				function()
727 				{
728 					return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.getWindow().$.getSelection() );
729 				},
730 
731 		/**
732 		 * Gets the type of the current selection. The following values are
733 		 * available:
734 		 * <ul>
735 		 *		<li><code>{@link CKEDITOR.SELECTION_NONE}</code> (1): No selection.</li>
736 		 *		<li><code>{@link CKEDITOR.SELECTION_TEXT}</code> (2): A text or a collapsed
737 		 *			selection is selected.</li>
738 		 *		<li><code>{@link CKEDITOR.SELECTION_ELEMENT}</code> (3): An element is
739 		 *			selected.</li>
740 		 * </ul>
741 		 * @function
742 		 * @returns {Number} One of the following constant values:
743 		 *		<code>{@link CKEDITOR.SELECTION_NONE}</code>, <code>{@link CKEDITOR.SELECTION_TEXT}</code>, or
744 		 *		<code>{@link CKEDITOR.SELECTION_ELEMENT}</code>.
745 		 * @example
746 		 * if ( editor.getSelection().<strong>getType()</strong> == CKEDITOR.SELECTION_TEXT )
747 		 *     alert( 'A text is selected' );
748 		 */
749 		getType :
750 			CKEDITOR.env.ie ?
751 				function()
752 				{
753 					var cache = this._.cache;
754 					if ( cache.type )
755 						return cache.type;
756 
757 					var type = CKEDITOR.SELECTION_NONE;
758 
759 					try
760 					{
761 						var sel = this.getNative(),
762 							ieType = sel.type;
763 
764 						if ( ieType == 'Text' )
765 							type = CKEDITOR.SELECTION_TEXT;
766 
767 						if ( ieType == 'Control' )
768 							type = CKEDITOR.SELECTION_ELEMENT;
769 
770 						// It is possible that we can still get a text range
771 						// object even when type == 'None' is returned by IE.
772 						// So we'd better check the object returned by
773 						// createRange() rather than by looking at the type.
774 						if ( sel.createRange().parentElement )
775 							type = CKEDITOR.SELECTION_TEXT;
776 					}
777 					catch(e) {}
778 
779 					return ( cache.type = type );
780 				}
781 			:
782 				function()
783 				{
784 					var cache = this._.cache;
785 					if ( cache.type )
786 						return cache.type;
787 
788 					var type = CKEDITOR.SELECTION_TEXT;
789 
790 					var sel = this.getNative();
791 
792 					if ( !sel )
793 						type = CKEDITOR.SELECTION_NONE;
794 					else if ( sel.rangeCount == 1 )
795 					{
796 						// Check if the actual selection is a control (IMG,
797 						// TABLE, HR, etc...).
798 
799 						var range = sel.getRangeAt(0),
800 							startContainer = range.startContainer;
801 
802 						if ( startContainer == range.endContainer
803 							&& startContainer.nodeType == 1
804 							&& ( range.endOffset - range.startOffset ) == 1
805 							&& styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] )
806 						{
807 							type = CKEDITOR.SELECTION_ELEMENT;
808 						}
809 					}
810 
811 					return ( cache.type = type );
812 				},
813 
814 		/**
815 		 * Retrieves the <code>{@link CKEDITOR.dom.range}</code> instances that represent the current selection.
816 		 * Note: Some browsers return multiple ranges even for a continuous selection. Firefox, for example, returns
817 		 * one range for each table cell when one or more table rows are selected.
818 		 * @function
819 		 * @param {Boolean} [onlyEditables] If set to <code>true</code>, this function retrives editable ranges only.
820 		 * @return {Array} Range instances that represent the current selection.
821 		 * @example
822 		 * var ranges = selection.<strong>getRanges()</strong>;
823 		 * alert( ranges.length );
824 		 */
825 		getRanges : (function()
826 		{
827 			var func = CKEDITOR.env.ie ?
828 				( function()
829 				{
830 					function getNodeIndex( node ) { return new CKEDITOR.dom.node( node ).getIndex(); }
831 
832 					// Finds the container and offset for a specific boundary
833 					// of an IE range.
834 					var getBoundaryInformation = function( range, start )
835 					{
836 						// Creates a collapsed range at the requested boundary.
837 						range = range.duplicate();
838 						range.collapse( start );
839 
840 						// Gets the element that encloses the range entirely.
841 						var parent = range.parentElement(),
842 							doc = parent.ownerDocument;
843 
844 						// Empty parent element, e.g. <i>^</i>
845 						if ( !parent.hasChildNodes() )
846 							return  { container : parent, offset : 0 };
847 
848 						var siblings = parent.children,
849 							child,
850 							sibling,
851 							testRange = range.duplicate(),
852 							startIndex = 0,
853 							endIndex = siblings.length - 1,
854 							index = -1,
855 							position,
856 							distance,
857 							container;
858 
859 						// Binary search over all element childs to test the range to see whether
860 						// range is right on the boundary of one element.
861 						while ( startIndex <= endIndex )
862 						{
863 							index = Math.floor( ( startIndex + endIndex ) / 2 );
864 							child = siblings[ index ];
865 							testRange.moveToElementText( child );
866 							position = testRange.compareEndPoints( 'StartToStart', range );
867 
868 							if ( position > 0 )
869 								endIndex = index - 1;
870 							else if ( position < 0 )
871 								startIndex = index + 1;
872 							else
873 							{
874 								// IE9 report wrong measurement with compareEndPoints when range anchors between two BRs.
875 								// e.g. <p>text<br />^<br /></p> (#7433)
876 								if ( CKEDITOR.env.ie9Compat && child.tagName == 'BR' )
877 								{
878 									// "Fall back" to w3c selection.
879 									var sel = doc.defaultView.getSelection();
880 									return { container : sel[ start ? 'anchorNode' : 'focusNode' ],
881 										offset : sel[ start ? 'anchorOffset' : 'focusOffset' ] };
882 								}
883 								else
884 									return { container : parent, offset : getNodeIndex( child ) };
885 							}
886 						}
887 
888 						// All childs are text nodes,
889 						// or to the right hand of test range are all text nodes. (#6992)
890 						if ( index == -1 || index == siblings.length - 1 && position < 0 )
891 						{
892 							// Adapt test range to embrace the entire parent contents.
893 							testRange.moveToElementText( parent );
894 							testRange.setEndPoint( 'StartToStart', range );
895 
896 							// IE report line break as CRLF with range.text but
897 							// only LF with textnode.nodeValue, normalize them to avoid
898 							// breaking character counting logic below. (#3949)
899 							distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
900 
901 							siblings = parent.childNodes;
902 
903 							// Actual range anchor right beside test range at the boundary of text node.
904 							if ( !distance )
905 							{
906 								child = siblings[ siblings.length - 1 ];
907 
908 								if ( child.nodeType != CKEDITOR.NODE_TEXT )
909 									return { container : parent, offset : siblings.length };
910 								else
911 									return { container : child, offset : child.nodeValue.length };
912 							}
913 
914 							// Start the measuring until distance overflows, meanwhile count the text nodes.
915 							var i = siblings.length;
916 							while ( distance > 0 && i > 0 )
917 							{
918 								sibling = siblings[ --i ];
919 								if ( sibling.nodeType == CKEDITOR.NODE_TEXT )
920 								{
921 									container = sibling;
922 									distance -= sibling.nodeValue.length;
923 								}
924 							}
925 
926 							return  { container : container, offset : -distance };
927 						}
928 						// Test range was one offset beyond OR behind the anchored text node.
929 						else
930 						{
931 							// Adapt one side of test range to the actual range
932 							// for measuring the offset between them.
933 							testRange.collapse( position > 0 ? true : false );
934 							testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
935 
936 							// IE report line break as CRLF with range.text but
937 							// only LF with textnode.nodeValue, normalize them to avoid
938 							// breaking character counting logic below. (#3949)
939 							distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
940 
941 							// Actual range anchor right beside test range at the inner boundary of text node.
942 							if ( !distance )
943 								return { container : parent, offset : getNodeIndex( child ) + ( position > 0 ? 0 : 1 ) };
944 
945 							// Start the measuring until distance overflows, meanwhile count the text nodes.
946 							while ( distance > 0 )
947 							{
948 								try
949 								{
950 									sibling = child[ position > 0 ? 'previousSibling' : 'nextSibling' ];
951 									if ( sibling.nodeType == CKEDITOR.NODE_TEXT )
952 									{
953 										distance -= sibling.nodeValue.length;
954 										container = sibling;
955 									}
956 									child = sibling;
957 								}
958 								// Measurement in IE could be somtimes wrong because of <select> element. (#4611)
959 								catch( e )
960 								{
961 									return { container : parent, offset : getNodeIndex( child ) };
962 								}
963 							}
964 
965 							return { container : container, offset : position > 0 ? -distance : container.nodeValue.length + distance };
966 						}
967 					};
968 
969 					return function()
970 					{
971 						// IE doesn't have range support (in the W3C way), so we
972 						// need to do some magic to transform selections into
973 						// CKEDITOR.dom.range instances.
974 
975 						var sel = this.getNative(),
976 							nativeRange = sel && sel.createRange(),
977 							type = this.getType(),
978 							range;
979 
980 						if ( !sel )
981 							return [];
982 
983 						if ( type == CKEDITOR.SELECTION_TEXT )
984 						{
985 							range = new CKEDITOR.dom.range( this.document );
986 
987 							var boundaryInfo = getBoundaryInformation( nativeRange, true );
988 							range.setStart( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
989 
990 							boundaryInfo = getBoundaryInformation( nativeRange );
991 							range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
992 
993 							// Correct an invalid IE range case on empty list item. (#5850)
994 							if ( range.endContainer.getPosition( range.startContainer ) & CKEDITOR.POSITION_PRECEDING
995 									&& range.endOffset <= range.startContainer.getIndex() )
996 							{
997 								range.collapse();
998 							}
999 
1000 							return [ range ];
1001 						}
1002 						else if ( type == CKEDITOR.SELECTION_ELEMENT )
1003 						{
1004 							var retval = [];
1005 
1006 							for ( var i = 0 ; i < nativeRange.length ; i++ )
1007 							{
1008 								var element = nativeRange.item( i ),
1009 									parentElement = element.parentNode,
1010 									j = 0;
1011 
1012 								range = new CKEDITOR.dom.range( this.document );
1013 
1014 								for (; j < parentElement.childNodes.length && parentElement.childNodes[j] != element ; j++ )
1015 								{ /*jsl:pass*/ }
1016 
1017 								range.setStart( new CKEDITOR.dom.node( parentElement ), j );
1018 								range.setEnd( new CKEDITOR.dom.node( parentElement ), j + 1 );
1019 								retval.push( range );
1020 							}
1021 
1022 							return retval;
1023 						}
1024 
1025 						return [];
1026 					};
1027 				})()
1028 			:
1029 				function()
1030 				{
1031 
1032 					// On browsers implementing the W3C range, we simply
1033 					// tranform the native ranges in CKEDITOR.dom.range
1034 					// instances.
1035 
1036 					var ranges = [],
1037 						range,
1038 						doc = this.document,
1039 						sel = this.getNative();
1040 
1041 					if ( !sel )
1042 						return ranges;
1043 
1044 					// On WebKit, it may happen that we'll have no selection
1045 					// available. We normalize it here by replicating the
1046 					// behavior of other browsers.
1047 					if ( !sel.rangeCount )
1048 					{
1049 						range = new CKEDITOR.dom.range( doc );
1050 						range.moveToElementEditStart( doc.getBody() );
1051 						ranges.push( range );
1052 					}
1053 
1054 					for ( var i = 0 ; i < sel.rangeCount ; i++ )
1055 					{
1056 						var nativeRange = sel.getRangeAt( i );
1057 
1058 						range = new CKEDITOR.dom.range( doc );
1059 
1060 						range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset );
1061 						range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset );
1062 						ranges.push( range );
1063 					}
1064 					return ranges;
1065 				};
1066 
1067 			return function( onlyEditables )
1068 			{
1069 				var cache = this._.cache;
1070 				if ( cache.ranges && !onlyEditables )
1071 					return cache.ranges;
1072 				else if ( !cache.ranges )
1073 					cache.ranges = new CKEDITOR.dom.rangeList( func.call( this ) );
1074 
1075 				// Split range into multiple by read-only nodes.
1076 				if ( onlyEditables )
1077 				{
1078 					var ranges = cache.ranges;
1079 					for ( var i = 0; i < ranges.length; i++ )
1080 					{
1081 						var range = ranges[ i ];
1082 
1083 						// Drop range spans inside one ready-only node.
1084 						var parent = range.getCommonAncestor();
1085 						if ( parent.isReadOnly() )
1086 							ranges.splice( i, 1 );
1087 
1088 						if ( range.collapsed )
1089 							continue;
1090 
1091 						// Range may start inside a non-editable element,
1092 						// replace the range start after it.
1093 						if ( range.startContainer.isReadOnly() )
1094 						{
1095 							var current = range.startContainer;
1096 							while( current )
1097 							{
1098 								if ( current.is( 'body' ) || !current.isReadOnly() )
1099 									break;
1100 
1101 								if ( current.type == CKEDITOR.NODE_ELEMENT
1102 										&& current.getAttribute( 'contentEditable' ) == 'false' )
1103 									range.setStartAfter( current );
1104 
1105 								current = current.getParent();
1106 							}
1107 						}
1108 
1109 						var startContainer = range.startContainer,
1110 							endContainer = range.endContainer,
1111 							startOffset = range.startOffset,
1112 							endOffset = range.endOffset,
1113 							walkerRange = range.clone();
1114 
1115 						// Enlarge range start/end with text node to avoid walker
1116 						// being DOM destructive, it doesn't interfere our checking
1117 						// of elements below as well.
1118 						if ( startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
1119 						{
1120 							if ( startOffset >= startContainer.getLength() )
1121 								walkerRange.setStartAfter( startContainer );
1122 							else
1123 								walkerRange.setStartBefore( startContainer );
1124 						}
1125 
1126 						if ( endContainer && endContainer.type == CKEDITOR.NODE_TEXT )
1127 						{
1128 							if ( !endOffset )
1129 								walkerRange.setEndBefore( endContainer );
1130 							else
1131 								walkerRange.setEndAfter( endContainer );
1132 						}
1133 
1134 						// Looking for non-editable element inside the range.
1135 						var walker = new CKEDITOR.dom.walker( walkerRange );
1136 						walker.evaluator = function( node )
1137 						{
1138 							if ( node.type == CKEDITOR.NODE_ELEMENT
1139 								&& node.isReadOnly() )
1140 							{
1141 								var newRange = range.clone();
1142 								range.setEndBefore( node );
1143 
1144 								// Drop collapsed range around read-only elements,
1145 								// it make sure the range list empty when selecting
1146 								// only non-editable elements.
1147 								if ( range.collapsed )
1148 									ranges.splice( i--, 1 );
1149 
1150 								// Avoid creating invalid range.
1151 								if ( !( node.getPosition( walkerRange.endContainer ) & CKEDITOR.POSITION_CONTAINS ) )
1152 								{
1153 									newRange.setStartAfter( node );
1154 									if ( !newRange.collapsed )
1155 										ranges.splice( i + 1, 0, newRange );
1156 								}
1157 
1158 								return true;
1159 							}
1160 
1161 							return false;
1162 						};
1163 
1164 						walker.next();
1165 					}
1166 				}
1167 
1168 				return cache.ranges;
1169 			};
1170 		})(),
1171 
1172 		/**
1173 		 * Gets the DOM element in which the selection starts.
1174 		 * @returns {CKEDITOR.dom.element} The element at the beginning of the
1175 		 *		selection.
1176 		 * @example
1177 		 * var element = editor.getSelection().<strong>getStartElement()</strong>;
1178 		 * alert( element.getName() );
1179 		 */
1180 		getStartElement : function()
1181 		{
1182 			var cache = this._.cache;
1183 			if ( cache.startElement !== undefined )
1184 				return cache.startElement;
1185 
1186 			var node,
1187 				sel = this.getNative();
1188 
1189 			switch ( this.getType() )
1190 			{
1191 				case CKEDITOR.SELECTION_ELEMENT :
1192 					return this.getSelectedElement();
1193 
1194 				case CKEDITOR.SELECTION_TEXT :
1195 
1196 					var range = this.getRanges()[0];
1197 
1198 					if ( range )
1199 					{
1200 						if ( !range.collapsed )
1201 						{
1202 							range.optimize();
1203 
1204 							// Decrease the range content to exclude particial
1205 							// selected node on the start which doesn't have
1206 							// visual impact. ( #3231 )
1207 							while ( 1 )
1208 							{
1209 								var startContainer = range.startContainer,
1210 									startOffset = range.startOffset;
1211 								// Limit the fix only to non-block elements.(#3950)
1212 								if ( startOffset == ( startContainer.getChildCount ?
1213 									 startContainer.getChildCount() : startContainer.getLength() )
1214 									 && !startContainer.isBlockBoundary() )
1215 									range.setStartAfter( startContainer );
1216 								else break;
1217 							}
1218 
1219 							node = range.startContainer;
1220 
1221 							if ( node.type != CKEDITOR.NODE_ELEMENT )
1222 								return node.getParent();
1223 
1224 							node = node.getChild( range.startOffset );
1225 
1226 							if ( !node || node.type != CKEDITOR.NODE_ELEMENT )
1227 								node = range.startContainer;
1228 							else
1229 							{
1230 								var child = node.getFirst();
1231 								while (  child && child.type == CKEDITOR.NODE_ELEMENT )
1232 								{
1233 									node = child;
1234 									child = child.getFirst();
1235 								}
1236 							}
1237 						}
1238 						else
1239 						{
1240 							node = range.startContainer;
1241 							if ( node.type != CKEDITOR.NODE_ELEMENT )
1242 								node = node.getParent();
1243 						}
1244 
1245 						node = node.$;
1246 					}
1247 			}
1248 
1249 			return cache.startElement = ( node ? new CKEDITOR.dom.element( node ) : null );
1250 		},
1251 
1252 		/**
1253 		 * Gets the currently selected element.
1254 		 * @returns {CKEDITOR.dom.element} The selected element. Null if no
1255 		 *		selection is available or the selection type is not
1256 		 *		<code>{@link CKEDITOR.SELECTION_ELEMENT}</code>.
1257 		 * @example
1258 		 * var element = editor.getSelection().<strong>getSelectedElement()</strong>;
1259 		 * alert( element.getName() );
1260 		 */
1261 		getSelectedElement : function()
1262 		{
1263 			var cache = this._.cache;
1264 			if ( cache.selectedElement !== undefined )
1265 				return cache.selectedElement;
1266 
1267 			var self = this;
1268 
1269 			var node = CKEDITOR.tools.tryThese(
1270 				// Is it native IE control type selection?
1271 				function()
1272 				{
1273 					return self.getNative().createRange().item( 0 );
1274 				},
1275 				// If a table or list is fully selected.
1276 				function()
1277 				{
1278 					var root,
1279 						retval,
1280 						range  = self.getRanges()[ 0 ],
1281 						ancestor = range.getCommonAncestor( 1, 1 ),
1282 						tags = { table:1,ul:1,ol:1,dl:1 };
1283 
1284 					for ( var t in tags )
1285 					{
1286 						if ( ( root = ancestor.getAscendant( t, 1 ) ) )
1287 							break;
1288 					}
1289 
1290 					if ( root )
1291 					{
1292 						// Enlarging the start boundary.
1293 						var testRange = new CKEDITOR.dom.range( this.document );
1294 						testRange.setStartAt( root, CKEDITOR.POSITION_AFTER_START );
1295 						testRange.setEnd( range.startContainer, range.startOffset );
1296 
1297 						var enlargeables = CKEDITOR.tools.extend( tags, CKEDITOR.dtd.$listItem, CKEDITOR.dtd.$tableContent ),
1298 							walker = new CKEDITOR.dom.walker( testRange ),
1299 							// Check the range is at the inner boundary of the structural element.
1300 							guard = function( walker, isEnd )
1301 							{
1302 								return function( node, isWalkOut )
1303 								{
1304 									if ( node.type == CKEDITOR.NODE_TEXT && ( !CKEDITOR.tools.trim( node.getText() ) || node.getParent().data( 'cke-bookmark' ) ) )
1305 										return true;
1306 
1307 									var tag;
1308 									if ( node.type == CKEDITOR.NODE_ELEMENT )
1309 									{
1310 										tag = node.getName();
1311 
1312 										// Bypass bogus br at the end of block.
1313 										if ( tag == 'br' && isEnd && node.equals( node.getParent().getBogus() ) )
1314 											return true;
1315 
1316 										if ( isWalkOut && tag in enlargeables || tag in CKEDITOR.dtd.$removeEmpty )
1317 											return true;
1318 									}
1319 
1320 									walker.halted = 1;
1321 									return false;
1322 								};
1323 							};
1324 
1325 						walker.guard = guard( walker );
1326 
1327 						if ( walker.checkBackward() && !walker.halted )
1328 						{
1329 							walker = new CKEDITOR.dom.walker( testRange );
1330 							testRange.setStart( range.endContainer, range.endOffset );
1331 							testRange.setEndAt( root, CKEDITOR.POSITION_BEFORE_END );
1332 							walker.guard = guard( walker, 1 );
1333 							if ( walker.checkForward() && !walker.halted )
1334 								retval = root.$;
1335 						}
1336 					}
1337 
1338 					if ( !retval )
1339 						throw 0;
1340 
1341 					return retval;
1342 				},
1343 				// Figure it out by checking if there's a single enclosed
1344 				// node of the range.
1345 				function()
1346 				{
1347 					var range  = self.getRanges()[ 0 ],
1348 						enclosed,
1349 						selected;
1350 
1351 					// Check first any enclosed element, e.g. <ul>[<li><a href="#">item</a></li>]</ul>
1352 					for ( var i = 2; i && !( ( enclosed = range.getEnclosedNode() )
1353 						&& ( enclosed.type == CKEDITOR.NODE_ELEMENT )
1354 						&& styleObjectElements[ enclosed.getName() ]
1355 						&& ( selected = enclosed ) ); i-- )
1356 					{
1357 						// Then check any deep wrapped element, e.g. [<b><i><img /></i></b>]
1358 						range.shrink( CKEDITOR.SHRINK_ELEMENT );
1359 					}
1360 
1361 					return  selected.$;
1362 				});
1363 
1364 			return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null );
1365 		},
1366 
1367 		/**
1368 		 * Retrieves the text contained within the range. An empty string is returned for non-text selection.
1369 		 * @returns {String} A string of text within the current selection.
1370 		 * @since 3.6.1
1371 		 * @example
1372 		 * var text = editor.getSelection().<strong>getSelectedText()</strong>;
1373 		 * alert( text );
1374 		 */
1375 		getSelectedText : function()
1376 		{
1377 			var cache = this._.cache;
1378 			if ( cache.selectedText !== undefined )
1379 				return cache.selectedText;
1380 
1381 			var text = '',
1382 				nativeSel = this.getNative();
1383 			if ( this.getType() == CKEDITOR.SELECTION_TEXT )
1384 				text = CKEDITOR.env.ie ? nativeSel.createRange().text : nativeSel.toString();
1385 
1386 			return ( cache.selectedText = text );
1387 		},
1388 
1389 		/**
1390 		 * Locks the selection made in the editor in order to make it possible to
1391 		 * manipulate it without browser interference. A locked selection is
1392 		 * cached and remains unchanged until it is released with the <code>#unlock</code>
1393 		 * method.
1394 		 * @example
1395 		 * editor.getSelection().<strong>lock()</strong>;
1396 		 */
1397 		lock : function()
1398 		{
1399 			// Call all cacheable function.
1400 			this.getRanges();
1401 			this.getStartElement();
1402 			this.getSelectedElement();
1403 			this.getSelectedText();
1404 
1405 			// The native selection is not available when locked.
1406 			this._.cache.nativeSel = {};
1407 
1408 			this.isLocked = 1;
1409 
1410 			// Save this selection inside the DOM document.
1411 			this.document.setCustomData( 'cke_locked_selection', this );
1412 		},
1413 
1414 		/**
1415 		 * Unlocks the selection made in the editor and locked with the <code>#lock</code> method.
1416 		 * An unlocked selection is no longer cached and can be changed.
1417 		 * @param {Boolean} [restore] If set to <code>true</code>, the selection is restored back to the selection saved earlier by using the <code>#lock</code> method.
1418 		 * @example
1419 		 * editor.getSelection().<strong>unlock()</strong>;
1420 		 */
1421 		unlock : function( restore )
1422 		{
1423 			var doc = this.document,
1424 				lockedSelection = doc.getCustomData( 'cke_locked_selection' );
1425 
1426 			if ( lockedSelection )
1427 			{
1428 				doc.setCustomData( 'cke_locked_selection', null );
1429 
1430 				if ( restore )
1431 				{
1432 					var selectedElement = lockedSelection.getSelectedElement(),
1433 						ranges = !selectedElement && lockedSelection.getRanges();
1434 
1435 					this.isLocked = 0;
1436 					this.reset();
1437 
1438 					doc.getBody().focus();
1439 
1440 					if ( selectedElement )
1441 						this.selectElement( selectedElement );
1442 					else
1443 						this.selectRanges( ranges );
1444 				}
1445 			}
1446 
1447 			if  ( !lockedSelection || !restore )
1448 			{
1449 				this.isLocked = 0;
1450 				this.reset();
1451 			}
1452 		},
1453 
1454 		/**
1455 		 * Clears the selection cache.
1456 		 * @example
1457 		 * editor.getSelection().<strong>reset()</strong>;
1458 		 */
1459 		reset : function()
1460 		{
1461 			this._.cache = {};
1462 		},
1463 
1464 		/**
1465 		 * Makes the current selection of type <code>{@link CKEDITOR.SELECTION_ELEMENT}</code> by enclosing the specified element.
1466 		 * @param {CKEDITOR.dom.element} element The element to enclose in the selection.
1467 		 * @example
1468 		 * var element = editor.document.getById( 'sampleElement' );
1469 		 * editor.getSelection.<strong>selectElement( element )</strong>;
1470 		 */
1471 		selectElement : function( element )
1472 		{
1473 			if ( this.isLocked )
1474 			{
1475 				var range = new CKEDITOR.dom.range( this.document );
1476 				range.setStartBefore( element );
1477 				range.setEndAfter( element );
1478 
1479 				this._.cache.selectedElement = element;
1480 				this._.cache.startElement = element;
1481 				this._.cache.ranges = new CKEDITOR.dom.rangeList( range );
1482 				this._.cache.type = CKEDITOR.SELECTION_ELEMENT;
1483 
1484 				return;
1485 			}
1486 
1487 			range = new CKEDITOR.dom.range( element.getDocument() );
1488 			range.setStartBefore( element );
1489 			range.setEndAfter( element );
1490 			range.select();
1491 
1492 			this.document.fire( 'selectionchange' );
1493 			this.reset();
1494 
1495 		},
1496 
1497 		/**
1498 		 *  Clears the original selection and adds the specified ranges
1499 		 * to the document selection.
1500 		 * @param {Array} ranges An array of <code>{@link CKEDITOR.dom.range}</code> instances representing ranges to be added to the document.
1501 		 * @example
1502 		 * var ranges = new CKEDITOR.dom.range( editor.document );
1503 		 * editor.getSelection().<strong>selectRanges( [ ranges ] )</strong>;
1504 		 */
1505 		selectRanges : function( ranges )
1506 		{
1507 			if ( this.isLocked )
1508 			{
1509 				this._.cache.selectedElement = null;
1510 				this._.cache.startElement = ranges[ 0 ] && ranges[ 0 ].getTouchedStartNode();
1511 				this._.cache.ranges = new CKEDITOR.dom.rangeList( ranges );
1512 				this._.cache.type = CKEDITOR.SELECTION_TEXT;
1513 
1514 				return;
1515 			}
1516 
1517 			if ( CKEDITOR.env.ie )
1518 			{
1519 				if ( ranges.length > 1 )
1520 				{
1521 					// IE doesn't accept multiple ranges selection, so we join all into one.
1522 					var last = ranges[ ranges.length -1 ] ;
1523 					ranges[ 0 ].setEnd( last.endContainer, last.endOffset );
1524 					ranges.length = 1;
1525 				}
1526 
1527 				if ( ranges[ 0 ] )
1528 					ranges[ 0 ].select();
1529 
1530 				this.reset();
1531 			}
1532 			else
1533 			{
1534 				var sel = this.getNative();
1535 
1536 				// getNative() returns null if iframe is "display:none" in FF. (#6577)
1537 				if ( !sel )
1538 					return;
1539 
1540 				if ( ranges.length )
1541 				{
1542 					sel.removeAllRanges();
1543 					// Remove any existing filling char first.
1544 					CKEDITOR.env.webkit && removeFillingChar( this.document );
1545 				}
1546 
1547 				for ( var i = 0 ; i < ranges.length ; i++ )
1548 				{
1549 					// Joining sequential ranges introduced by
1550 					// readonly elements protection.
1551 					if ( i < ranges.length -1 )
1552 					{
1553 						var left = ranges[ i ], right = ranges[ i +1 ],
1554 								between = left.clone();
1555 						between.setStart( left.endContainer, left.endOffset );
1556 						between.setEnd( right.startContainer, right.startOffset );
1557 
1558 						// Don't confused by Firefox adjancent multi-ranges
1559 						// introduced by table cells selection.
1560 						if ( !between.collapsed )
1561 						{
1562 							between.shrink( CKEDITOR.NODE_ELEMENT, true );
1563 							var ancestor = between.getCommonAncestor(),
1564 								enclosed = between.getEnclosedNode();
1565 
1566 							// The following cases has to be considered:
1567 							// 1. <span contenteditable="false">[placeholder]</span>
1568 							// 2. <input contenteditable="false"  type="radio"/> (#6621)
1569 							if ( ancestor.isReadOnly() || enclosed && enclosed.isReadOnly() )
1570 							{
1571 								right.setStart( left.startContainer, left.startOffset );
1572 								ranges.splice( i--, 1 );
1573 								continue;
1574 							}
1575 						}
1576 					}
1577 
1578 					var range = ranges[ i ];
1579 					var nativeRange = this.document.$.createRange();
1580 					var startContainer = range.startContainer;
1581 
1582 					// In FF2, if we have a collapsed range, inside an empty
1583 					// element, we must add something to it otherwise the caret
1584 					// will not be visible.
1585 					// In Opera instead, the selection will be moved out of the
1586 					// element. (#4657)
1587 					if ( range.collapsed &&
1588 						( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) ) &&
1589 						startContainer.type == CKEDITOR.NODE_ELEMENT &&
1590 						!startContainer.getChildCount() )
1591 					{
1592 						startContainer.appendText( '' );
1593 					}
1594 
1595 					if ( range.collapsed
1596 							&& CKEDITOR.env.webkit
1597 							&& rangeRequiresFix( range ) )
1598 					{
1599 						// Append a zero-width space so WebKit will not try to
1600 						// move the selection by itself (#1272).
1601 						var fillingChar = createFillingChar( this.document );
1602 						range.insertNode( fillingChar ) ;
1603 
1604 						var next = fillingChar.getNext();
1605 
1606 						// If the filling char is followed by a <br>, whithout
1607 						// having something before it, it'll not blink.
1608 						// Let's remove it in this case.
1609 						if ( next && !fillingChar.getPrevious() && next.type == CKEDITOR.NODE_ELEMENT && next.getName() == 'br' )
1610 						{
1611 							removeFillingChar( this.document );
1612 							range.moveToPosition( next, CKEDITOR.POSITION_BEFORE_START );
1613 						}
1614 						else
1615 							range.moveToPosition( fillingChar, CKEDITOR.POSITION_AFTER_END );
1616 					}
1617 
1618 					nativeRange.setStart( range.startContainer.$, range.startOffset );
1619 
1620 					try
1621 					{
1622 						nativeRange.setEnd( range.endContainer.$, range.endOffset );
1623 					}
1624 					catch ( e )
1625 					{
1626 						// There is a bug in Firefox implementation (it would be too easy
1627 						// otherwise). The new start can't be after the end (W3C says it can).
1628 						// So, let's create a new range and collapse it to the desired point.
1629 						if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 )
1630 						{
1631 							range.collapse( 1 );
1632 							nativeRange.setEnd( range.endContainer.$, range.endOffset );
1633 						}
1634 						else
1635 							throw e;
1636 					}
1637 
1638 					// Select the range.
1639 					sel.addRange( nativeRange );
1640 				}
1641 
1642 				// Don't miss selection change event for non-IEs.
1643 				this.document.fire( 'selectionchange' );
1644 				this.reset();
1645 			}
1646 		},
1647 
1648 		/**
1649 		 *  Creates a bookmark for each range of this selection (from <code>#getRanges</code>)
1650 		 * by calling the <code>{@link CKEDITOR.dom.range.prototype.createBookmark}</code> method,
1651 		 * with extra care taken to avoid interference among those ranges. The arguments
1652 		 * received are the same as with the underlying range method.
1653 		 * @returns {Array} Array of bookmarks for each range.
1654 		 * @example
1655 		 * var bookmarks = editor.getSelection().<strong>createBookmarks()</strong>;
1656 		 */
1657 		createBookmarks : function( serializable )
1658 		{
1659 			return this.getRanges().createBookmarks( serializable );
1660 		},
1661 
1662 		/**
1663 		 *  Creates a bookmark for each range of this selection (from <code>#getRanges</code>)
1664 		 * by calling the <code>{@link CKEDITOR.dom.range.prototype.createBookmark2}</code> method,
1665 		 * with extra care taken to avoid interference among those ranges. The arguments
1666 		 * received are the same as with the underlying range method.
1667 		 * @returns {Array} Array of bookmarks for each range.
1668 		 * @example
1669 		 * var bookmarks = editor.getSelection().<strong>createBookmarks2()</strong>;
1670 		 */
1671 		createBookmarks2 : function( normalized )
1672 		{
1673 			return this.getRanges().createBookmarks2( normalized );
1674 		},
1675 
1676 		/**
1677 		 * Selects the virtual ranges denoted by the bookmarks by calling <code>#selectRanges</code>.
1678 		 * @param {Array} bookmarks The bookmarks representing ranges to be selected.
1679 		 * @returns {CKEDITOR.dom.selection} This selection object, after the ranges were selected.
1680 		 * @example
1681 		 * var bookmarks = editor.getSelection().createBookmarks();
1682 		 * editor.getSelection().<strong>selectBookmarks( bookmarks )</strong>;
1683 		 */
1684 		selectBookmarks : function( bookmarks )
1685 		{
1686 			var ranges = [];
1687 			for ( var i = 0 ; i < bookmarks.length ; i++ )
1688 			{
1689 				var range = new CKEDITOR.dom.range( this.document );
1690 				range.moveToBookmark( bookmarks[i] );
1691 				ranges.push( range );
1692 			}
1693 			this.selectRanges( ranges );
1694 			return this;
1695 		},
1696 
1697 		/**
1698 		 * Retrieves the common ancestor node of the first range and the last range.
1699 		 * @returns {CKEDITOR.dom.element} The common ancestor of the selection.
1700 		 * @example
1701 		 * var ancestor = editor.getSelection().<strong>getCommonAncestor()</strong>;
1702 		 */
1703 		getCommonAncestor : function()
1704 		{
1705 			var ranges = this.getRanges(),
1706 				startNode = ranges[ 0 ].startContainer,
1707 				endNode = ranges[ ranges.length - 1 ].endContainer;
1708 			return startNode.getCommonAncestor( endNode );
1709 		},
1710 
1711 		/**
1712 		 * Moves the scrollbar to the starting position of the current selection.
1713 		 * @example
1714 		 * editor.getSelection().<strong>scrollIntoView()</strong>;
1715 		 */
1716 		scrollIntoView : function()
1717 		{
1718 			// If we have split the block, adds a temporary span at the
1719 			// range position and scroll relatively to it.
1720 			var start = this.getStartElement();
1721 			start.scrollIntoView();
1722 		}
1723 	};
1724 })();
1725 
1726 ( function()
1727 {
1728 	var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
1729 			fillerTextRegex = /\ufeff|\u00a0/,
1730 			nonCells = { table:1,tbody:1,tr:1 };
1731 
1732 	CKEDITOR.dom.range.prototype.select =
1733 		CKEDITOR.env.ie ?
1734 			// V2
1735 			function( forceExpand )
1736 			{
1737 				var collapsed = this.collapsed,
1738 					isStartMarkerAlone, dummySpan, ieRange;
1739 
1740 				// Try to make a object selection.
1741 				var selected = this.getEnclosedNode();
1742 				if ( selected )
1743 				{
1744 					try
1745 					{
1746 						ieRange = this.document.$.body.createControlRange();
1747 						ieRange.addElement( selected.$ );
1748 						ieRange.select();
1749 						return;
1750 					}
1751 					catch( er ) {}
1752 				}
1753 
1754 				// IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g.
1755 				// <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>...
1756 				if ( this.startContainer.type == CKEDITOR.NODE_ELEMENT && this.startContainer.getName() in nonCells
1757 					|| this.endContainer.type == CKEDITOR.NODE_ELEMENT && this.endContainer.getName() in nonCells )
1758 				{
1759 					this.shrink( CKEDITOR.NODE_ELEMENT, true );
1760 				}
1761 
1762 				var bookmark = this.createBookmark();
1763 
1764 				// Create marker tags for the start and end boundaries.
1765 				var startNode = bookmark.startNode;
1766 
1767 				var endNode;
1768 				if ( !collapsed )
1769 					endNode = bookmark.endNode;
1770 
1771 				// Create the main range which will be used for the selection.
1772 				ieRange = this.document.$.body.createTextRange();
1773 
1774 				// Position the range at the start boundary.
1775 				ieRange.moveToElementText( startNode.$ );
1776 				ieRange.moveStart( 'character', 1 );
1777 
1778 				if ( endNode )
1779 				{
1780 					// Create a tool range for the end.
1781 					var ieRangeEnd = this.document.$.body.createTextRange();
1782 
1783 					// Position the tool range at the end.
1784 					ieRangeEnd.moveToElementText( endNode.$ );
1785 
1786 					// Move the end boundary of the main range to match the tool range.
1787 					ieRange.setEndPoint( 'EndToEnd', ieRangeEnd );
1788 					ieRange.moveEnd( 'character', -1 );
1789 				}
1790 				else
1791 				{
1792 					// The isStartMarkerAlone logic comes from V2. It guarantees that the lines
1793 					// will expand and that the cursor will be blinking on the right place.
1794 					// Actually, we are using this flag just to avoid using this hack in all
1795 					// situations, but just on those needed.
1796 					var next = startNode.getNext( notWhitespaces );
1797 					isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) )     // already a filler there?
1798 										  && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) );
1799 
1800 					// Append a temporary <span></span> before the selection.
1801 					// This is needed to avoid IE destroying selections inside empty
1802 					// inline elements, like <b></b> (#253).
1803 					// It is also needed when placing the selection right after an inline
1804 					// element to avoid the selection moving inside of it.
1805 					dummySpan = this.document.createElement( 'span' );
1806 					dummySpan.setHtml( '' );	// Zero Width No-Break Space (U+FEFF). See #1359.
1807 					dummySpan.insertBefore( startNode );
1808 
1809 					if ( isStartMarkerAlone )
1810 					{
1811 						// To expand empty blocks or line spaces after <br>, we need
1812 						// instead to have any char, which will be later deleted using the
1813 						// selection.
1814 						// \ufeff = Zero Width No-Break Space (U+FEFF). (#1359)
1815 						this.document.createText( '\ufeff' ).insertBefore( startNode );
1816 					}
1817 				}
1818 
1819 				// Remove the markers (reset the position, because of the changes in the DOM tree).
1820 				this.setStartBefore( startNode );
1821 				startNode.remove();
1822 
1823 				if ( collapsed )
1824 				{
1825 					if ( isStartMarkerAlone )
1826 					{
1827 						// Move the selection start to include the temporary \ufeff.
1828 						ieRange.moveStart( 'character', -1 );
1829 
1830 						ieRange.select();
1831 
1832 						// Remove our temporary stuff.
1833 						this.document.$.selection.clear();
1834 					}
1835 					else
1836 						ieRange.select();
1837 
1838 					this.moveToPosition( dummySpan, CKEDITOR.POSITION_BEFORE_START );
1839 					dummySpan.remove();
1840 				}
1841 				else
1842 				{
1843 					this.setEndBefore( endNode );
1844 					endNode.remove();
1845 					ieRange.select();
1846 				}
1847 
1848 				this.document.fire( 'selectionchange' );
1849 			}
1850 		:
1851 			function()
1852 			{
1853 				this.document.getSelection().selectRanges( [ this ] );
1854 			};
1855 } )();
1856