/**
* 选集
* @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"]();
}
};
})();