/** * 选集 * @file * @module UE.dom * @class Selection * @since 1.2.6.1 */ /** * 选区集合 * @unfile * @module UE.dom * @class Selection */ (function() { function getBoundaryInformation(range, start) { var getIndex = domUtils.getNodeIndex; range = range.duplicate(); range.collapse(start); var parent = range.parentElement(); //如果节点里没有子节点,直接退出 if (!parent.hasChildNodes()) { return { container: parent, offset: 0 }; } var siblings = parent.children, child, testRange = range.duplicate(), startIndex = 0, endIndex = siblings.length - 1, index = -1, distance; while (startIndex <= endIndex) { index = Math.floor((startIndex + endIndex) / 2); child = siblings[index]; testRange.moveToElementText(child); var position = testRange.compareEndPoints("StartToStart", range); if (position > 0) { endIndex = index - 1; } else if (position < 0) { startIndex = index + 1; } else { //trace:1043 return { container: parent, offset: getIndex(child) }; } } if (index == -1) { testRange.moveToElementText(parent); testRange.setEndPoint("StartToStart", range); distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length; siblings = parent.childNodes; if (!distance) { child = siblings[siblings.length - 1]; return { container: child, offset: child.nodeValue.length }; } var i = siblings.length; while (distance > 0) { distance -= siblings[--i].nodeValue.length; } return { container: siblings[i], offset: -distance }; } testRange.collapse(position > 0); testRange.setEndPoint(position > 0 ? "StartToStart" : "EndToStart", range); distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length; if (!distance) { return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ? { container: parent, offset: getIndex(child) + (position > 0 ? 0 : 1) } : { container: child, offset: position > 0 ? 0 : child.childNodes.length }; } while (distance > 0) { try { var pre = child; child = child[position > 0 ? "previousSibling" : "nextSibling"]; distance -= child.nodeValue.length; } catch (e) { return { container: parent, offset: getIndex(pre) }; } } return { container: child, offset: position > 0 ? -distance : child.nodeValue.length + distance }; } /** * 将ieRange转换为Range对象 * @param {Range} ieRange ieRange对象 * @param {Range} range Range对象 * @return {Range} range 返回转换后的Range对象 */ function transformIERangeToRange(ieRange, range) { if (ieRange.item) { range.selectNode(ieRange.item(0)); } else { var bi = getBoundaryInformation(ieRange, true); range.setStart(bi.container, bi.offset); if (ieRange.compareEndPoints("StartToEnd", ieRange) != 0) { bi = getBoundaryInformation(ieRange, false); range.setEnd(bi.container, bi.offset); } } return range; } /** * 获得ieRange * @param {Selection} sel Selection对象 * @return {ieRange} 得到ieRange */ function _getIERange(sel) { var ieRange; //ie下有可能报错 try { ieRange = sel.getNative().createRange(); } catch (e) { return null; } var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); if ((el.ownerDocument || el) === sel.document) { return ieRange; } return null; } var Selection = (dom.Selection = function(doc) { var me = this, iframe; me.document = doc; if (browser.ie9below) { iframe = domUtils.getWindow(doc).frameElement; domUtils.on(iframe, "beforedeactivate", function() { me._bakIERange = me.getIERange(); }); domUtils.on(iframe, "activate", function() { try { if (!_getIERange(me) && me._bakIERange) { me._bakIERange.select(); } } catch (ex) {} me._bakIERange = null; }); } iframe = doc = null; }); Selection.prototype = { rangeInBody: function(rng, txtRange) { var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer; return node === this.document.body || domUtils.inDoc(node, this.document); }, /** * 获取原生seleciton对象 * @method getNative * @return { Object } 获得selection对象 * @example * ```javascript * editor.selection.getNative(); * ``` */ getNative: function() { var doc = this.document; try { return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow(doc).getSelection(); } catch (e) { return null; } }, /** * 获得ieRange * @method getIERange * @return { Object } 返回ie原生的Range * @example * ```javascript * editor.selection.getIERange(); * ``` */ getIERange: function() { var ieRange = _getIERange(this); if (!ieRange) { if (this._bakIERange) { return this._bakIERange; } } return ieRange; }, /** * 缓存当前选区的range和选区的开始节点 * @method cache */ cache: function() { this.clear(); this._cachedRange = this.getRange(); this._cachedStartElement = this.getStart(); this._cachedStartElementPath = this.getStartElementPath(); }, /** * 获取选区开始位置的父节点到body * @method getStartElementPath * @return { Array } 返回父节点集合 * @example * ```javascript * editor.selection.getStartElementPath(); * ``` */ getStartElementPath: function() { if (this._cachedStartElementPath) { return this._cachedStartElementPath; } var start = this.getStart(); if (start) { return domUtils.findParents(start, true, null, true); } return []; }, /** * 清空缓存 * @method clear */ clear: function() { this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null; }, /** * 编辑器是否得到了选区 * @method isFocus */ isFocus: function() { try { if (browser.ie9below) { var nativeRange = _getIERange(this); return !!(nativeRange && this.rangeInBody(nativeRange)); } else { return !!this.getNative().rangeCount; } } catch (e) { return false; } }, /** * 获取选区对应的Range * @method getRange * @return { Object } 得到Range对象 * @example * ```javascript * editor.selection.getRange(); * ``` */ getRange: function() { var me = this; function optimze(range) { var child = me.document.body.firstChild, collapsed = range.collapsed; while (child && child.firstChild) { range.setStart(child, 0); child = child.firstChild; } if (!range.startContainer) { range.setStart(me.document.body, 0); } if (collapsed) { range.collapse(true); } } if (me._cachedRange != null) { return this._cachedRange; } var range = new baidu.editor.dom.Range(me.document); if (browser.ie9below) { var nativeRange = me.getIERange(); if (nativeRange) { //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置 try { transformIERangeToRange(nativeRange, range); } catch (e) { optimze(range); } } else { optimze(range); } } else { var sel = me.getNative(); if (sel && sel.rangeCount) { var firstRange = sel.getRangeAt(0); var lastRange = sel.getRangeAt(sel.rangeCount - 1); range .setStart(firstRange.startContainer, firstRange.startOffset) .setEnd(lastRange.endContainer, lastRange.endOffset); if ( range.collapsed && domUtils.isBody(range.startContainer) && !range.startOffset ) { optimze(range); } } else { //trace:1734 有可能已经不在dom树上了,标识的节点 if ( this._bakRange && domUtils.inDoc(this._bakRange.startContainer, this.document) ) { return this._bakRange; } optimze(range); } } return (this._bakRange = range); }, /** * 获取开始元素,用于状态反射 * @method getStart * @return { Element } 获得开始元素 * @example * ```javascript * editor.selection.getStart(); * ``` */ getStart: function() { if (this._cachedStartElement) { return this._cachedStartElement; } var range = browser.ie9below ? this.getIERange() : this.getRange(), tmpRange, start, tmp, parent; if (browser.ie9below) { if (!range) { //todo 给第一个值可能会有问题 return this.document.body.firstChild; } //control元素 if (range.item) { return range.item(0); } tmpRange = range.duplicate(); //修正ie下x[xx] 闭合后 x|xx tmpRange.text.length > 0 && tmpRange.moveStart("character", 1); tmpRange.collapse(1); start = tmpRange.parentElement(); parent = tmp = range.parentElement(); while ((tmp = tmp.parentNode)) { if (tmp == start) { start = parent; break; } } } else { range.shrinkBoundary(); start = range.startContainer; if (start.nodeType == 1 && start.hasChildNodes()) { start = start.childNodes[ Math.min(start.childNodes.length - 1, range.startOffset) ]; } if (start.nodeType == 3) { return start.parentNode; } } return start; }, /** * 得到选区中的文本 * @method getText * @return { String } 选区中包含的文本 * @example * ```javascript * editor.selection.getText(); * ``` */ getText: function() { var nativeSel, nativeRange; if (this.isFocus() && (nativeSel = this.getNative())) { nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt(0); return browser.ie9below ? nativeRange.text : nativeRange.toString(); } return ""; }, /** * 清除选区 * @method clearRange * @example * ```javascript * editor.selection.clearRange(); * ``` */ clearRange: function() { this.getNative()[browser.ie9below ? "empty" : "removeAllRanges"](); } }; })();