1 /** 2 * modified from ckeditor core - selection 3 * @author yiminghe@gmail.com 4 */ 5 /* 6 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. 7 For licensing, see LICENSE.html or http://ckeditor.com/license 8 */ 9 KISSY.add("editor/core/selection", function (S) { 10 11 var Editor = S.Editor; 12 13 /** 14 * selection type enum 15 * @enum {number} 16 */ 17 Editor.SELECTION = { 18 SELECTION_NONE:1, 19 SELECTION_TEXT:2, 20 SELECTION_ELEMENT:3 21 22 }; 23 var TRUE = true, 24 FALSE = false, 25 NULL = null, 26 UA = S.UA, 27 DOM = S.DOM, 28 //tryThese = Editor.Utils.tryThese, 29 Node = S.Node, 30 KES = Editor.SELECTION, 31 KER = Editor.RANGE, 32 // ie9 仍然采用老的 range api,发现新的不稳定 33 OLD_IE = UA['ie'], //!window.getSelection, 34 //EventTarget = S.EventTarget, 35 Walker = Editor.Walker, 36 //ElementPath = Editor.ElementPath, 37 KERange = Editor.Range; 38 39 /** 40 * @constructor 41 * @param document {Document} 42 */ 43 function KESelection(document) { 44 var self = this; 45 self.document = document; 46 self._ = { 47 cache:{} 48 }; 49 50 /** 51 * IE BUG: The selection's document may be a different document than the 52 * editor document. Return NULL if that's the case. 53 */ 54 if (OLD_IE) { 55 try { 56 var range = self.getNative().createRange(); 57 if (!range 58 || ( range.item && range.item(0).ownerDocument != document ) 59 || ( range.parentElement && range.parentElement().ownerDocument != document )) { 60 self.isInvalid = TRUE; 61 } 62 } 63 // 2012-06-13 发布页 bug 64 // 当焦点在一个跨域的 iframe 内,调用该操作抛拒绝访问异常 65 catch (e) { 66 self.isInvalid = TRUE; 67 } 68 } 69 } 70 71 var styleObjectElements = { 72 img:1, hr:1, li:1, table:1, tr:1, td:1, th:1, embed:1, object:1, ol:1, ul:1, 73 a:1, input:1, form:1, select:1, textarea:1, button:1, fieldset:1, thead:1, tfoot:1 74 }; 75 76 S.augment(KESelection, { 77 78 79 /** 80 * Gets the native selection object from the browser. 81 * @returns {Object} The native selection object. 82 * @example 83 * var selection = editor.getSelection().<b>getNative()</b>; 84 */ 85 getNative:!OLD_IE ? 86 function () { 87 var self = this, 88 cache = self._.cache; 89 return cache.nativeSel || ( cache.nativeSel = DOM._getWin(self.document).getSelection() ); 90 } 91 : 92 function () { 93 var self = this, cache = self._.cache; 94 return cache.nativeSel || ( cache.nativeSel = self.document.selection ); 95 }, 96 97 /** 98 * Gets the type of the current selection. The following values are 99 * available: 100 * <ul> 101 * <li> SELECTION_NONE (1): No selection.</li> 102 * <li> SELECTION_TEXT (2): Text is selected or 103 * collapsed selection.</li> 104 * <li> SELECTION_ELEMENT (3): A element 105 * selection.</li> 106 * </ul> 107 * @returns {number} One of the following constant values: 108 * SELECTION_NONE, SELECTION_TEXT or 109 * SELECTION_ELEMENT. 110 * @example 111 * if ( editor.getSelection().<b>getType()</b> == SELECTION_TEXT ) 112 * alert( 'Text is selected' ); 113 */ 114 getType:!OLD_IE ? 115 function () { 116 var self = this, cache = self._.cache; 117 if (cache.type) 118 return cache.type; 119 120 var type = KES.SELECTION_TEXT, 121 sel = self.getNative(); 122 123 if (!sel) 124 type = KES.SELECTION_NONE; 125 else if (sel.rangeCount == 1) { 126 // Check if the actual selection is a control (IMG, 127 // TABLE, HR, etc...). 128 129 var range = sel.getRangeAt(0), 130 startContainer = range.startContainer; 131 132 if (startContainer == range.endContainer 133 && startContainer.nodeType == DOM.ELEMENT_NODE 134 && Number(range.endOffset - range.startOffset) == 1 135 && styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ]) { 136 type = KES.SELECTION_ELEMENT; 137 } 138 } 139 140 return ( cache.type = type ); 141 } : 142 function () { 143 var self = this, cache = self._.cache; 144 if (cache.type) 145 return cache.type; 146 147 var type = KES.SELECTION_NONE; 148 149 try { 150 var sel = self.getNative(), 151 ieType = sel.type; 152 153 if (ieType == 'Text') 154 type = KES.SELECTION_TEXT; 155 156 if (ieType == 'Control') 157 type = KES.SELECTION_ELEMENT; 158 159 // It is possible that we can still get a text range 160 // object even when type == 'None' is returned by IE. 161 // So we'd better check the object returned by 162 // createRange() rather than by looking at the type. 163 //当前一个操作选中文本,后一个操作右键点了字串中间就会出现了 164 if (sel.createRange().parentElement) 165 type = KES.SELECTION_TEXT; 166 } 167 catch (e) { 168 } 169 170 return ( cache.type = type ); 171 }, 172 173 getRanges:OLD_IE ? 174 (function () { 175 // Finds the container and offset for a specific boundary 176 // of an IE range. 177 /** 178 * 179 * @param {TextRange} range 180 * @param {Boolean=} start 181 */ 182 var getBoundaryInformation = function (range, start) { 183 // Creates a collapsed range at the requested boundary. 184 range = range.duplicate(); 185 range.collapse(start); 186 187 // Gets the element that encloses the range entirely. 188 var parent = range.parentElement(), siblings = parent.childNodes, 189 testRange; 190 191 for (var i = 0; i < siblings.length; i++) { 192 var child = siblings[ i ]; 193 194 if (child.nodeType == DOM.ELEMENT_NODE) { 195 testRange = range.duplicate(); 196 197 testRange.moveToElementText(child); 198 199 var comparisonStart = testRange.compareEndPoints('StartToStart', range), 200 comparisonEnd = testRange.compareEndPoints('EndToStart', range); 201 202 testRange.collapse(); 203 //中间有其他标签 204 if (comparisonStart > 0) 205 break; 206 // When selection stay at the side of certain self-closing elements, e.g. BR, 207 // our comparison will never shows an equality. (#4824) 208 else if (!comparisonStart 209 || comparisonEnd == 1 && comparisonStart == -1) 210 return { container:parent, offset:i }; 211 else if (!comparisonEnd) 212 return { container:parent, offset:i + 1 }; 213 214 testRange = NULL; 215 } 216 } 217 218 if (!testRange) { 219 testRange = range.duplicate(); 220 testRange.moveToElementText(parent); 221 testRange.collapse(FALSE); 222 } 223 224 testRange.setEndPoint('StartToStart', range); 225 // IE report line break as CRLF with range.text but 226 // only LF with textnode.nodeValue, normalize them to avoid 227 // breaking character counting logic below. (#3949) 228 var distance = String(testRange.text) 229 .replace(/\r\n|\r/g, '\n').length; 230 231 try { 232 while (distance > 0) 233 //bug? 可能不是文本节点 nodeValue undefined 234 //永远不会出现 textnode<img/>textnode 235 //停止时,前面一定为textnode 236 distance -= siblings[ --i ].nodeValue.length; 237 } 238 // Measurement in IE could be somtimes wrong because of <select> element. (#4611) 239 catch (e) { 240 distance = 0; 241 } 242 243 244 if (distance === 0) { 245 return { 246 container:parent, 247 offset:i 248 }; 249 } 250 else { 251 return { 252 container:siblings[ i ], 253 offset:-distance 254 }; 255 } 256 }; 257 258 return function (force) { 259 var self = this, cache = self._.cache; 260 if (cache.ranges && !force) 261 return cache.ranges; 262 263 // IE doesn't have range support (in the W3C way), so we 264 // need to do some magic to transform selections into 265 // Range instances. 266 267 var sel = self.getNative(), 268 nativeRange = sel && sel.createRange(), 269 type = self.getType(), 270 range; 271 272 if (!sel) 273 return []; 274 275 if (type == KES.SELECTION_TEXT) { 276 range = new KERange(self.document); 277 var boundaryInfo = getBoundaryInformation(nativeRange, TRUE); 278 range.setStart(new Node(boundaryInfo.container), boundaryInfo.offset); 279 boundaryInfo = getBoundaryInformation(nativeRange); 280 range.setEnd(new Node(boundaryInfo.container), boundaryInfo.offset); 281 return ( cache.ranges = [ range ] ); 282 } else if (type == KES.SELECTION_ELEMENT) { 283 var retval = cache.ranges = []; 284 285 for (var i = 0; i < nativeRange.length; i++) { 286 var element = nativeRange.item(i), 287 parentElement = element.parentNode, 288 j = 0; 289 290 range = new KERange(self.document); 291 292 for (; j < parentElement.childNodes.length && parentElement.childNodes[j] != element; j++) { /*jsl:pass*/ 293 } 294 295 range.setStart(new Node(parentElement), j); 296 range.setEnd(new Node(parentElement), j + 1); 297 retval.push(range); 298 } 299 300 return retval; 301 } 302 303 return ( cache.ranges = [] ); 304 }; 305 })() 306 : 307 function (force) { 308 var self = this, cache = self._.cache; 309 if (cache.ranges && !force) 310 return cache.ranges; 311 312 // On browsers implementing the W3C range, we simply 313 // tranform the native ranges in Range 314 // instances. 315 316 var ranges = [], sel = self.getNative(); 317 318 if (!sel) 319 return []; 320 321 for (var i = 0; i < sel.rangeCount; i++) { 322 var nativeRange = sel.getRangeAt(i), range = new KERange(self.document); 323 324 range.setStart(new Node(nativeRange.startContainer), nativeRange.startOffset); 325 range.setEnd(new Node(nativeRange.endContainer), nativeRange.endOffset); 326 ranges.push(range); 327 } 328 329 return ( cache.ranges = ranges ); 330 }, 331 332 /** 333 * Gets the DOM element in which the selection starts. 334 * @returns The element at the beginning of the 335 * selection. 336 * @example 337 * var element = editor.getSelection().<b>getStartElement()</b>; 338 * alert( element.nodeName() ); 339 */ 340 getStartElement:function () { 341 var self = this, cache = self._.cache; 342 if (cache.startElement !== undefined) 343 return cache.startElement; 344 345 var node, 346 sel = self.getNative(); 347 348 switch (self.getType()) { 349 case KES.SELECTION_ELEMENT : 350 return this.getSelectedElement(); 351 352 case KES.SELECTION_TEXT : 353 354 var range = self.getRanges()[0]; 355 356 if (range) { 357 if (!range.collapsed) { 358 range.optimize(); 359 360 // Decrease the range content to exclude particial 361 // selected node on the start which doesn't have 362 // visual impact. ( #3231 ) 363 while (TRUE) { 364 var startContainer = range.startContainer, 365 startOffset = range.startOffset; 366 // Limit the fix only to non-block elements.(#3950) 367 if (startOffset == ( startContainer[0].nodeType === DOM.ELEMENT_NODE ? 368 startContainer[0].childNodes.length : startContainer[0].nodeValue.length ) 369 && !startContainer._4e_isBlockBoundary()) { 370 range.setStartAfter(startContainer); 371 } else { 372 break; 373 } 374 } 375 376 node = range.startContainer; 377 378 if (node[0].nodeType != DOM.ELEMENT_NODE) { 379 return node.parent(); 380 } 381 382 node = new Node(node[0].childNodes[range.startOffset]); 383 384 if (!node[0] || node[0].nodeType != DOM.ELEMENT_NODE) { 385 return range.startContainer; 386 } 387 388 var child = node[0].firstChild; 389 while (child && child.nodeType == DOM.ELEMENT_NODE) { 390 node = new Node(child); 391 child = child.firstChild; 392 } 393 return node; 394 } 395 } 396 397 if (OLD_IE) { 398 range = sel.createRange(); 399 range.collapse(TRUE); 400 node = new Node(range.parentElement()); 401 } 402 else { 403 node = sel.anchorNode; 404 if (node && node.nodeType != DOM.ELEMENT_NODE) { 405 node = node.parentNode; 406 } 407 if (node) { 408 node = new Node(node); 409 } 410 } 411 } 412 413 return cache.startElement = node; 414 }, 415 416 /** 417 * Gets the current selected element. 418 * @returns The selected element. Null if no 419 * selection is available or the selection type is not 420 * SELECTION_ELEMENT. 421 * @example 422 * var element = editor.getSelection().<b>getSelectedElement()</b>; 423 * alert( element.nodeName() ); 424 */ 425 getSelectedElement:function () { 426 var self = this, 427 node, 428 cache = self._.cache; 429 430 if (cache.selectedElement !== undefined) { 431 return cache.selectedElement; 432 } 433 434 // Is it native IE control type selection? 435 if (OLD_IE) { 436 var range = self.getNative().createRange(); 437 node = range.item && range.item(0); 438 } 439 440 // Figure it out by checking if there's a single enclosed 441 // node of the range. 442 // 处理 ^ <img/> ^ 443 if (!node) { 444 node = (function () { 445 var range = self.getRanges()[ 0 ], 446 enclosed, 447 selected; 448 449 // 先检查第一层 450 // <div>^<img/>^</div> 451 // shrink 再检查 452 // <div><span>^<img/>^</span></div> 453 for (var i = 2; 454 i && !(( enclosed = range.getEnclosedNode() ) && 455 ( enclosed[0].nodeType == DOM.ELEMENT_NODE ) && 456 // 某些值得这么多的元素?? 457 styleObjectElements[ enclosed.nodeName() ] && 458 ( selected = enclosed )); 459 i--) { 460 // Then check any deep wrapped element 461 // e.g. [<b><i><img /></i></b>] 462 // 一下子退到底 ^<a><span><span><img/></span></span></a>^ 463 // -> 464 //<a><span><span>^<img/>^</span></span></a> 465 range.shrink(KER.SHRINK_ELEMENT); 466 } 467 468 return selected; 469 })(); 470 } else { 471 node = new Node(node); 472 } 473 474 return cache.selectedElement = node; 475 }, 476 477 478 reset:function () { 479 this._.cache = {}; 480 }, 481 482 selectElement:function (element) { 483 var range, 484 self = this, 485 doc = self.document; 486 if (OLD_IE) { 487 //do not use empty(),编辑器内滚动条重置了 488 //选择的 img 内容前后莫名被清除 489 //self.getNative().empty(); 490 try { 491 // Try to select the node as a control. 492 range = doc.body['createControlRange'](); 493 range['addElement'](element[0]); 494 range.select(); 495 } catch (e) { 496 // If failed, select it as a text range. 497 range = doc.body.createTextRange(); 498 range.moveToElementText(element[0]); 499 range.select(); 500 } finally { 501 // fire('selectionChange'); 502 } 503 self.reset(); 504 } else { 505 // Create the range for the element. 506 range = doc.createRange(); 507 range.selectNode(element[0]); 508 // Select the range. 509 var sel = self.getNative(); 510 sel.removeAllRanges(); 511 sel.addRange(range); 512 self.reset(); 513 } 514 }, 515 516 selectRanges:function (ranges) { 517 var self = this; 518 if (OLD_IE) { 519 if (ranges.length > 1) { 520 // IE doesn't accept multiple ranges selection, so we join all into one. 521 var last = ranges[ ranges.length - 1 ]; 522 ranges[ 0 ].setEnd(last.endContainer, last.endOffset); 523 ranges.length = 1; 524 } 525 526 // IE doesn't accept multiple ranges selection, so we just 527 // select the first one. 528 if (ranges[ 0 ]) 529 ranges[ 0 ].select(); 530 531 self.reset(); 532 } 533 else { 534 var sel = self.getNative(); 535 if (!sel) { 536 return; 537 } 538 sel.removeAllRanges(); 539 for (var i = 0; i < ranges.length; i++) { 540 var range = ranges[ i ], 541 nativeRange = self.document.createRange(), 542 startContainer = range.startContainer; 543 544 // In FF2, if we have a collapsed range, inside an empty 545 // element, we must add something to it otherwise the caret 546 // will not be visible. 547 // opera move out of this element 548 if (range.collapsed && 549 (( UA.gecko && UA.gecko < 1.0900 ) || UA.opera || UA['webkit']) && 550 startContainer[0].nodeType == DOM.ELEMENT_NODE && 551 !startContainer[0].childNodes.length) { 552 // webkit 光标停留不到在空元素内,要fill char,之后范围定在 fillchar 之后 553 startContainer[0].appendChild(self.document.createTextNode(UA['webkit'] ? "\u200b" : "")); 554 range.startOffset++; 555 range.endOffset++; 556 } 557 558 nativeRange.setStart(startContainer[0], range.startOffset); 559 nativeRange.setEnd(range.endContainer[0], range.endOffset); 560 // Select the range. 561 sel.addRange(nativeRange); 562 } 563 self.reset(); 564 } 565 }, 566 createBookmarks2:function (normalized) { 567 var bookmarks = [], 568 ranges = this.getRanges(); 569 570 for (var i = 0; i < ranges.length; i++) 571 bookmarks.push(ranges[i].createBookmark2(normalized)); 572 573 return bookmarks; 574 }, 575 createBookmarks:function (serializable, ranges) { 576 var self = this, 577 retval = [], 578 doc = self.document, 579 bookmark; 580 ranges = ranges || self.getRanges(); 581 var length = ranges.length; 582 for (var i = 0; i < length; i++) { 583 retval.push(bookmark = ranges[ i ].createBookmark(serializable, TRUE)); 584 serializable = bookmark.serializable; 585 586 var bookmarkStart = serializable ? S.one("#" + bookmark.startNode, doc) : bookmark.startNode, 587 bookmarkEnd = serializable ? S.one("#" + bookmark.endNode, doc) : bookmark.endNode; 588 589 // Updating the offset values for rest of ranges which have been mangled(#3256). 590 for (var j = i + 1; j < length; j++) { 591 var dirtyRange = ranges[ j ], 592 rangeStart = dirtyRange.startContainer, 593 rangeEnd = dirtyRange.endContainer; 594 595 DOM.equals(rangeStart, bookmarkStart.parent()) && dirtyRange.startOffset++; 596 DOM.equals(rangeStart, bookmarkEnd.parent()) && dirtyRange.startOffset++; 597 DOM.equals(rangeEnd, bookmarkStart.parent()) && dirtyRange.endOffset++; 598 DOM.equals(rangeEnd, bookmarkEnd.parent()) && dirtyRange.endOffset++; 599 } 600 } 601 602 return retval; 603 }, 604 605 selectBookmarks:function (bookmarks) { 606 var self = this, ranges = []; 607 for (var i = 0; i < bookmarks.length; i++) { 608 var range = new KERange(self.document); 609 range.moveToBookmark(bookmarks[i]); 610 ranges.push(range); 611 } 612 self.selectRanges(ranges); 613 return self; 614 }, 615 616 getCommonAncestor:function () { 617 var ranges = this.getRanges(), 618 startNode = ranges[ 0 ].startContainer, 619 endNode = ranges[ ranges.length - 1 ].endContainer; 620 return startNode._4e_commonAncestor(endNode); 621 }, 622 623 // Moving scroll bar to the current selection's start position. 624 scrollIntoView:function () { 625 // If we have split the block, adds a temporary span at the 626 // range position and scroll relatively to it. 627 var start = this.getStartElement(); 628 start && start.scrollIntoView(undefined, false); 629 }, 630 removeAllRanges:function () { 631 var sel = this.getNative(); 632 if (!OLD_IE) { 633 sel && sel.removeAllRanges(); 634 } else { 635 sel && sel.clear(); 636 } 637 } 638 }); 639 640 641 var nonCells = { table:1, tbody:1, tr:1 }, notWhitespaces = Walker.whitespaces(TRUE), 642 fillerTextRegex = /\ufeff|\u00a0/; 643 KERange.prototype["select"] = 644 KERange.prototype.select = 645 !OLD_IE ? function () { 646 var self = this, startContainer = self.startContainer; 647 648 // If we have a collapsed range, inside an empty element, we must add 649 // something to it, otherwise the caret will not be visible. 650 if (self.collapsed && startContainer[0].nodeType == DOM.ELEMENT_NODE && !startContainer[0].childNodes.length) 651 startContainer[0].appendChild(self.document.createTextNode("")); 652 653 var nativeRange = self.document.createRange(); 654 nativeRange.setStart(startContainer[0], self.startOffset); 655 656 try { 657 nativeRange.setEnd(self.endContainer[0], self.endOffset); 658 } catch (e) { 659 // There is a bug in Firefox implementation (it would be too easy 660 // otherwise). The new start can't be after the end (W3C says it can). 661 // So, let's create a new range and collapse it to the desired point. 662 if (e.toString().indexOf('NS_ERROR_ILLEGAL_VALUE') >= 0) { 663 self.collapse(TRUE); 664 nativeRange.setEnd(self.endContainer[0], self.endOffset); 665 } 666 else 667 throw( e ); 668 } 669 670 var selection = getSelection(self.document).getNative(); 671 selection.removeAllRanges(); 672 selection.addRange(nativeRange); 673 } : // V2 674 function (forceExpand) { 675 676 var self = this, 677 collapsed = self.collapsed, 678 isStartMarkerAlone, 679 dummySpan; 680 //选的是元素,直接使用selectElement 681 //还是有差异的,特别是img选择框问题 682 if ( 683 //ie8 有问题?? 684 //UA['ie']Engine!=8 && 685 self.startContainer[0] === self.endContainer[0] 686 && self.endOffset - self.startOffset == 1) { 687 var selEl = self.startContainer[0].childNodes[self.startOffset]; 688 if (selEl.nodeType == DOM.ELEMENT_NODE) { 689 new KESelection(self.document).selectElement(new Node(selEl)); 690 return; 691 } 692 } 693 // IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g. 694 // <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>... 695 if (self.startContainer[0].nodeType == DOM.ELEMENT_NODE && 696 self.startContainer.nodeName() in nonCells 697 || self.endContainer[0].nodeType == DOM.ELEMENT_NODE && 698 self.endContainer.nodeName() in nonCells) { 699 self.shrink(KER.SHRINK_ELEMENT, TRUE); 700 } 701 702 var bookmark = self.createBookmark(), 703 // Create marker tags for the start and end boundaries. 704 startNode = bookmark.startNode, 705 endNode; 706 if (!collapsed) 707 endNode = bookmark.endNode; 708 709 // Create the main range which will be used for the selection. 710 var ieRange = self.document.body.createTextRange(); 711 712 // Position the range at the start boundary. 713 ieRange.moveToElementText(startNode[0]); 714 //跳过开始 bookmark 标签 715 ieRange.moveStart('character', 1); 716 717 if (endNode) { 718 // Create a tool range for the end. 719 var ieRangeEnd = self.document.body.createTextRange(); 720 // Position the tool range at the end. 721 ieRangeEnd.moveToElementText(endNode[0]); 722 // Move the end boundary of the main range to match the tool range. 723 ieRange.setEndPoint('EndToEnd', ieRangeEnd); 724 ieRange.moveEnd('character', -1); 725 } 726 else { 727 // The isStartMarkerAlone logic comes from V2. It guarantees that the lines 728 // will expand and that the cursor will be blinking on the right place. 729 // Actually, we are using this flag just to avoid using this hack in all 730 // situations, but just on those needed. 731 var next = startNode[0].nextSibling; 732 while (next && !notWhitespaces(next)) { 733 next = next.nextSibling; 734 } 735 isStartMarkerAlone = 736 ( 737 !( next && next.nodeValue && next.nodeValue.match(fillerTextRegex) ) // already a filler there? 738 && ( forceExpand 739 || 740 !startNode[0].previousSibling 741 || 742 ( 743 startNode[0].previousSibling && 744 DOM.nodeName(startNode[0].previousSibling) == 'br' 745 ) 746 ) 747 ); 748 749 // Append a temporary <span></span> before the selection. 750 // This is needed to avoid IE destroying selections inside empty 751 // inline elements, like <b></b> (#253). 752 // It is also needed when placing the selection right after an inline 753 // element to avoid the selection moving inside of it. 754 dummySpan = new Node(self.document.createElement('span')); 755 dummySpan.html(''); // Zero Width No-Break Space (U+FEFF). See #1359. 756 dummySpan.insertBefore(startNode); 757 if (isStartMarkerAlone) { 758 // To expand empty blocks or line spaces after <br>, we need 759 // instead to have any char, which will be later deleted using the 760 // selection. 761 // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359) 762 DOM.insertBefore(self.document.createTextNode('\ufeff'), startNode[0] || startNode); 763 } 764 } 765 766 // Remove the markers (reset the position, because of the changes in the DOM tree). 767 self.setStartBefore(startNode); 768 startNode._4e_remove(); 769 770 if (collapsed) { 771 if (isStartMarkerAlone) { 772 // Move the selection start to include the temporary \ufeff. 773 ieRange.moveStart('character', -1); 774 ieRange.select(); 775 // Remove our temporary stuff. 776 self.document.selection.clear(); 777 } else 778 ieRange.select(); 779 if (dummySpan) { 780 self.moveToPosition(dummySpan, KER.POSITION_BEFORE_START); 781 dummySpan._4e_remove(); 782 } 783 } 784 else { 785 self.setEndBefore(endNode); 786 endNode._4e_remove(); 787 ieRange.select(); 788 } 789 // fire('selectionChange'); 790 }; 791 792 793 function getSelection(doc) { 794 var sel = new KESelection(doc); 795 return ( !sel || sel.isInvalid ) ? NULL : sel; 796 } 797 798 KESelection.getSelection = getSelection; 799 800 Editor.Selection = KESelection; 801 802 return KESelection; 803 }, { 804 requires:['./base', './walker', './range', './dom'] 805 }); 806