var _START_TO_START = 0, _START_TO_END = 1, _END_TO_END = 2, _END_TO_START = 3, _BOOKMARK_ID = 0; function _updateCollapsed(range) { range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset); return range; } /** cloneContents: _copyAndDelete(this, true, false) extractContents: _copyAndDelete(this, true, true) deleteContents: _copyAndDelete(this, false, true) */ function _copyAndDelete(range, isCopy, isDelete) { var doc = range.doc, nodeList = []; //split a textNode function splitTextNode(node, startOffset, endOffset) { var length = node.nodeValue.length, centerNode; if (isCopy) { var cloneNode = node.cloneNode(true); if (startOffset > 0) { centerNode = cloneNode.splitText(startOffset); } else { centerNode = cloneNode; } if (endOffset < length) { centerNode.splitText(endOffset - startOffset); } } if (isDelete) { var center = node; if (startOffset > 0) { center = node.splitText(startOffset); range.setStart(node, startOffset); } if (endOffset < length) { var right = center.splitText(endOffset - startOffset); range.setEnd(right, 0); } nodeList.push(center); } return centerNode; } function removeNodes() { if (isDelete) { range.up().collapse(true); } for (var i = 0, len = nodeList.length; i < len; i++) { var node = nodeList[i]; if (node.parentNode) { node.parentNode.removeChild(node); } } } var copyRange = range.cloneRange().down(); var start = -1, incStart = -1, incEnd = -1, end = -1, ancestor = range.commonAncestor(), frag = doc.createDocumentFragment(); // startContainer is textNode and startContainer == endContainer if (ancestor.nodeType == 3) { var textNode = splitTextNode(ancestor, range.startOffset, range.endOffset); if (isCopy) { frag.appendChild(textNode); } removeNodes(); return isCopy ? frag : range; } // other case function extractNodes(parent, frag) { var node = parent.firstChild, nextNode; while (node) { var testRange = new KRange(doc).selectNode(node); start = testRange.compareBoundaryPoints(_START_TO_END, range); if (start >= 0 && incStart <= 0) { incStart = testRange.compareBoundaryPoints(_START_TO_START, range); } if (incStart >= 0 && incEnd <= 0) { incEnd = testRange.compareBoundaryPoints(_END_TO_END, range); } if (incEnd >= 0 && end <= 0) { end = testRange.compareBoundaryPoints(_END_TO_START, range); } if (end >= 0) { return false; } nextNode = node.nextSibling; if (start > 0) { if (node.nodeType == 1) { if (incStart >= 0 && incEnd <= 0) { if (isCopy) { frag.appendChild(node.cloneNode(true)); } if (isDelete) { nodeList.push(node); } } else { var childFlag; if (isCopy) { childFlag = node.cloneNode(false); frag.appendChild(childFlag); } if (extractNodes(node, childFlag) === false) { return false; } } } else if (node.nodeType == 3) { var textNode; if (node == copyRange.startContainer) { textNode = splitTextNode(node, copyRange.startOffset, node.nodeValue.length); } else if (node == copyRange.endContainer) { textNode = splitTextNode(node, 0, copyRange.endOffset); } else { textNode = splitTextNode(node, 0, node.nodeValue.length); } if (isCopy) { // TODO: IE9有时候报错 try { frag.appendChild(textNode); } catch(e) {} } } } node = nextNode; } } extractNodes(ancestor, frag); if (isDelete) { range.up().collapse(true); } for (var i = 0, len = nodeList.length; i < len; i++) { var node = nodeList[i]; if (node.parentNode) { node.parentNode.removeChild(node); } } return isCopy ? frag : range; } //在marquee、select元素里不能使用moveToElementText,IE专用 function _moveToElementText(range, el) { var node = el; while (node) { var knode = K(node); if (knode.name == 'marquee' || knode.name == 'select') { return; } node = node.parentNode; } // IE有时候报错,屏蔽错误 try { range.moveToElementText(el); } catch(e) {} } //根据原生Range,取得开始节点和结束节点的位置。IE专用 function _getStartEnd(rng, isStart) { var doc = rng.parentElement().ownerDocument, pointRange = rng.duplicate(); pointRange.collapse(isStart); var parent = pointRange.parentElement(), nodes = parent.childNodes; if (nodes.length === 0) { return {node: parent.parentNode, offset: K(parent).index()}; } var startNode = doc, startPos = 0, cmp = -1; var testRange = rng.duplicate(); _moveToElementText(testRange, parent); for (var i = 0, len = nodes.length; i < len; i++) { var node = nodes[i]; cmp = testRange.compareEndPoints('StartToStart', pointRange); if (cmp === 0) { return {node: node.parentNode, offset: i}; } if (node.nodeType == 1) { var nodeRange = rng.duplicate(), dummy, knode = K(node), newNode = node; //
ab[cd]ef if (knode.isControl()) { dummy = doc.createElement('span'); knode.after(dummy); newNode = dummy; // 0123456[7]89
123
startPos += knode.text().replace(/\r\n|\n|\r/g, '').length; } _moveToElementText(nodeRange, newNode); testRange.setEndPoint('StartToEnd', nodeRange); if (cmp > 0) { startPos += nodeRange.text.replace(/\r\n|\n|\r/g, '').length; } else { startPos = 0; } if (dummy) { K(dummy).remove(); } } else if (node.nodeType == 3) { testRange.moveStart('character', node.nodeValue.length); startPos += node.nodeValue.length; } if (cmp < 0) { startNode = node; } } //

abc|

if (cmp < 0 && startNode.nodeType == 1) { return {node: parent, offset: K(parent.lastChild).index() + 1}; } //

ab|c

if (cmp > 0) { while (startNode.nextSibling && startNode.nodeType == 1) { startNode = startNode.nextSibling; } } testRange = rng.duplicate(); _moveToElementText(testRange, parent); testRange.setEndPoint('StartToEnd', pointRange); startPos -= testRange.text.replace(/\r\n|\n|\r/g, '').length; // [textNode1][textNode2]ab|cd if (cmp > 0 && startNode.nodeType == 3) { var prevNode = startNode.previousSibling; while (prevNode && prevNode.nodeType == 3) { startPos -= prevNode.nodeValue.length; prevNode = prevNode.previousSibling; } } return {node: startNode, offset: startPos}; } //根据Node和offset,取得表示该位置的原生Range。IE专用 function _getEndRange(node, offset) { var doc = node.ownerDocument || node, range = doc.body.createTextRange(); if (doc == node) { range.collapse(true); return range; } if (node.nodeType == 1 && node.childNodes.length > 0) { var children = node.childNodes, isStart, child; if (offset === 0) { child = children[0]; isStart = true; } else { child = children[offset - 1]; isStart = false; } if (!child) { return range; } if (K(child).name === 'head') { if (offset === 1) { isStart = true; } if (offset === 2) { isStart = false; } range.collapse(isStart); return range; } if (child.nodeType == 1) { var kchild = K(child), span; if (kchild.isControl()) { span = doc.createElement('span'); if (isStart) { kchild.before(span); } else { kchild.after(span); } child = span; } _moveToElementText(range, child); range.collapse(isStart); if (span) { K(span).remove(); } return range; } node = child; offset = isStart ? 0 : child.nodeValue.length; } var dummy = doc.createElement('span'); K(node).before(dummy); _moveToElementText(range, dummy); range.moveStart('character', offset); K(dummy).remove(); return range; } // convert native Range to KRange function _toRange(rng) { var doc, range; // |
// to
|
function tr2td(start) { if (K(start.node).name == 'tr') { start.node = start.node.cells[start.offset]; start.offset = 0; } } // IE if (_IERANGE) { if (rng.item) { doc = _getDoc(rng.item(0)); range = new KRange(doc); range.selectNode(rng.item(0)); return range; } doc = rng.parentElement().ownerDocument; var start = _getStartEnd(rng, true), end = _getStartEnd(rng, false); tr2td(start); tr2td(end); range = new KRange(doc); range.setStart(start.node, start.offset); range.setEnd(end.node, end.offset); return range; } // other browser var startContainer = rng.startContainer; doc = startContainer.ownerDocument || startContainer; range = new KRange(doc); range.setStart(startContainer, rng.startOffset); range.setEnd(rng.endContainer, rng.endOffset); return range; } // create KRange class function KRange(doc) { this.init(doc); } _extend(KRange, { init : function(doc) { var self = this; self.startContainer = doc; self.startOffset = 0; self.endContainer = doc; self.endOffset = 0; self.collapsed = true; self.doc = doc; }, commonAncestor : function() { function getParents(node) { var parents = []; while (node) { parents.push(node); node = node.parentNode; } return parents; } var parentsA = getParents(this.startContainer), parentsB = getParents(this.endContainer), i = 0, lenA = parentsA.length, lenB = parentsB.length, parentA, parentB; while (++i) { parentA = parentsA[lenA - i]; parentB = parentsB[lenB - i]; if (!parentA || !parentB || parentA !== parentB) { break; } } return parentsA[lenA - i + 1]; }, setStart : function(node, offset) { var self = this, doc = self.doc; self.startContainer = node; self.startOffset = offset; if (self.endContainer === doc) { self.endContainer = node; self.endOffset = offset; } return _updateCollapsed(this); }, setEnd : function(node, offset) { var self = this, doc = self.doc; self.endContainer = node; self.endOffset = offset; if (self.startContainer === doc) { self.startContainer = node; self.startOffset = offset; } return _updateCollapsed(this); }, setStartBefore : function(node) { return this.setStart(node.parentNode || this.doc, K(node).index()); }, setStartAfter : function(node) { return this.setStart(node.parentNode || this.doc, K(node).index() + 1); }, setEndBefore : function(node) { return this.setEnd(node.parentNode || this.doc, K(node).index()); }, setEndAfter : function(node) { return this.setEnd(node.parentNode || this.doc, K(node).index() + 1); }, selectNode : function(node) { return this.setStartBefore(node).setEndAfter(node); }, selectNodeContents : function(node) { var knode = K(node); if (knode.type == 3 || knode.isSingle()) { return this.selectNode(node); } var children = knode.children(); if (children.length > 0) { return this.setStartBefore(children[0]).setEndAfter(children[children.length - 1]); } return this.setStart(node, 0).setEnd(node, 0); }, collapse : function(toStart) { if (toStart) { return this.setEnd(this.startContainer, this.startOffset); } return this.setStart(this.endContainer, this.endOffset); }, compareBoundaryPoints : function(how, range) { var rangeA = this.get(), rangeB = range.get(); if (_IERANGE) { var arr = {}; arr[_START_TO_START] = 'StartToStart'; arr[_START_TO_END] = 'EndToStart'; arr[_END_TO_END] = 'EndToEnd'; arr[_END_TO_START] = 'StartToEnd'; var cmp = rangeA.compareEndPoints(arr[how], rangeB); if (cmp !== 0) { return cmp; } var nodeA, nodeB, nodeC, posA, posB; if (how === _START_TO_START || how === _END_TO_START) { nodeA = this.startContainer; posA = this.startOffset; } if (how === _START_TO_END || how === _END_TO_END) { nodeA = this.endContainer; posA = this.endOffset; } if (how === _START_TO_START || how === _START_TO_END) { nodeB = range.startContainer; posB = range.startOffset; } if (how === _END_TO_END || how === _END_TO_START) { nodeB = range.endContainer; posB = range.endOffset; } // nodeA和nodeA相同时 if (nodeA === nodeB) { var diff = posA - posB; return diff > 0 ? 1 : (diff < 0 ? -1 : 0); } // nodeA是nodeB的祖先时 nodeC = nodeB; while (nodeC && nodeC.parentNode !== nodeA) { nodeC = nodeC.parentNode; } if (nodeC) { return K(nodeC).index() >= posA ? -1 : 1; } // nodeB是nodeA的祖先时 nodeC = nodeA; while (nodeC && nodeC.parentNode !== nodeB) { nodeC = nodeC.parentNode; } if (nodeC) { return K(nodeC).index() >= posB ? 1 : -1; } // nodeB的下一个节点是nodeA的祖先 nodeC = K(nodeB).next(); if (nodeC && nodeC.contains(nodeA)) { return 1; } // nodeA的下一个节点是nodeB的祖先 nodeC = K(nodeA).next(); if (nodeC && nodeC.contains(nodeB)) { return -1; } //其它情况,暂时不需要 } else { return rangeA.compareBoundaryPoints(how, rangeB); } }, cloneRange : function() { return new KRange(this.doc).setStart(this.startContainer, this.startOffset).setEnd(this.endContainer, this.endOffset); }, toString : function() { //TODO var rng = this.get(), str = _IERANGE ? rng.text : rng.toString(); return str.replace(/\r\n|\n|\r/g, ''); }, cloneContents : function() { return _copyAndDelete(this, true, false); }, deleteContents : function() { return _copyAndDelete(this, false, true); }, extractContents : function() { return _copyAndDelete(this, true, true); }, insertNode : function(node) { var self = this, sc = self.startContainer, so = self.startOffset, ec = self.endContainer, eo = self.endOffset, firstChild, lastChild, c, nodeCount = 1; //node为文档碎片时 if (node.nodeName.toLowerCase() === '#document-fragment') { firstChild = node.firstChild; lastChild = node.lastChild; nodeCount = node.childNodes.length; } //startContainer为element时 if (sc.nodeType == 1) { c = sc.childNodes[so]; if (c) { sc.insertBefore(node, c); //调整结束节点位置 if (sc === ec) { eo += nodeCount; } } else { sc.appendChild(node); } //startContainer为text时 } else if (sc.nodeType == 3) { if (so === 0) { sc.parentNode.insertBefore(node, sc); //调整结束节点位置 if (sc.parentNode === ec) { eo += nodeCount; } } else if (so >= sc.nodeValue.length) { if (sc.nextSibling) { sc.parentNode.insertBefore(node, sc.nextSibling); } else { sc.parentNode.appendChild(node); } } else { if (so > 0) { c = sc.splitText(so); } else { c = sc; } sc.parentNode.insertBefore(node, c); //调整结束节点位置 if (sc === ec) { ec = c; eo -= so; } } } if (firstChild) { self.setStartBefore(firstChild).setEndAfter(lastChild); } else { self.selectNode(node); } if (self.compareBoundaryPoints(_END_TO_END, self.cloneRange().setEnd(ec, eo)) >= 1) { return self; } return self.setEnd(ec, eo); }, surroundContents : function(node) { node.appendChild(this.extractContents()); return this.insertNode(node).selectNode(node); }, // 判断range是不是control range isControl : function() { var self = this, sc = self.startContainer, so = self.startOffset, ec = self.endContainer, eo = self.endOffset, rng; return sc.nodeType == 1 && sc === ec && so + 1 === eo && K(sc.childNodes[so]).isControl(); }, // get original range get : function(hasControlRange) { var self = this, doc = self.doc, node, rng; // not IE if (!_IERANGE) { rng = doc.createRange(); try { rng.setStart(self.startContainer, self.startOffset); rng.setEnd(self.endContainer, self.endOffset); } catch (e) {} return rng; } // IE control range if (hasControlRange && self.isControl()) { rng = doc.body.createControlRange(); rng.addElement(self.startContainer.childNodes[self.startOffset]); return rng; } // IE text range var range = self.cloneRange().down(); rng = doc.body.createTextRange(); rng.setEndPoint('StartToStart', _getEndRange(range.startContainer, range.startOffset)); rng.setEndPoint('EndToStart', _getEndRange(range.endContainer, range.endOffset)); return rng; }, html : function() { return K(this.cloneContents()).outer(); }, // 降低range的位置 //

123|abcdef

// postion(strong, 1) -> positon("abc", 0) // or //

abc|123def

// postion(strong, 1) -> positon("abc", 3) down : function() { var self = this; function downPos(node, pos, isStart) { if (node.nodeType != 1) { return; } var children = K(node).children(); if (children.length === 0) { return; } var left, right, child, offset; if (pos > 0) { left = children.eq(pos - 1); } if (pos < children.length) { right = children.eq(pos); } if (left && left.type == 3) { child = left[0]; offset = child.nodeValue.length; } if (right && right.type == 3) { child = right[0]; offset = 0; } if (!child) { return; } if (isStart) { self.setStart(child, offset); } else { self.setEnd(child, offset); } } downPos(self.startContainer, self.startOffset, true); downPos(self.endContainer, self.endOffset, false); return self; }, // 提高range的位置 //

123|abcdef

// positon("abc", 0) -> postion(strong, 1) // or //

abc|123def

// positon("abc", 3) -> postion(strong, 1) up : function() { var self = this; function upPos(node, pos, isStart) { if (node.nodeType != 3) { return; } if (pos === 0) { if (isStart) { self.setStartBefore(node); } else { self.setEndBefore(node); } } else if (pos == node.nodeValue.length) { if (isStart) { self.setStartAfter(node); } else { self.setEndAfter(node); } } } upPos(self.startContainer, self.startOffset, true); upPos(self.endContainer, self.endOffset, false); return self; }, // 扩大边界 //

[123abc]def

to

[123abc]def

enlarge : function(toBlock) { var self = this; self.up(); function enlargePos(node, pos, isStart) { var knode = K(node), parent; if (knode.type == 3 || _NOSPLIT_TAG_MAP[knode.name] || !toBlock && knode.isBlock()) { return; } if (pos === 0) { while (!knode.prev()) { parent = knode.parent(); if (!parent || _NOSPLIT_TAG_MAP[parent.name] || !toBlock && parent.isBlock()) { break; } knode = parent; } if (isStart) { self.setStartBefore(knode[0]); } else { self.setEndBefore(knode[0]); } } else if (pos == knode.children().length) { while (!knode.next()) { parent = knode.parent(); if (!parent || _NOSPLIT_TAG_MAP[parent.name] || !toBlock && parent.isBlock()) { break; } knode = parent; } if (isStart) { self.setStartAfter(knode[0]); } else { self.setEndAfter(knode[0]); } } } enlargePos(self.startContainer, self.startOffset, true); enlargePos(self.endContainer, self.endOffset, false); return self; }, // 缩小边界 // [

123

] to

[123]

shrink : function() { var self = this, child, collapsed = self.collapsed; while (self.startContainer.nodeType == 1 && (child = self.startContainer.childNodes[self.startOffset]) && child.nodeType == 1 && !K(child).isSingle()) { self.setStart(child, 0); } if (collapsed) { return self.collapse(collapsed); } while (self.endContainer.nodeType == 1 && self.endOffset > 0 && (child = self.endContainer.childNodes[self.endOffset - 1]) && child.nodeType == 1 && !K(child).isSingle()) { self.setEnd(child, child.childNodes.length); } return self; }, // 创建bookmark,通过插入临时节点标记位置 createBookmark : function(serialize) { var self = this, doc = self.doc, endNode, startNode = K('', doc)[0]; startNode.id = '__kindeditor_bookmark_start_' + (_BOOKMARK_ID++) + '__'; if (!self.collapsed) { endNode = startNode.cloneNode(true); endNode.id = '__kindeditor_bookmark_end_' + (_BOOKMARK_ID++) + '__'; } if (endNode) { self.cloneRange().collapse(false).insertNode(endNode).setEndBefore(endNode); } self.insertNode(startNode).setStartAfter(startNode); return { start : serialize ? '#' + startNode.id : startNode, end : endNode ? (serialize ? '#' + endNode.id : endNode) : null }; }, // 根据bookmark重新设置range moveToBookmark : function(bookmark) { var self = this, doc = self.doc, start = K(bookmark.start, doc), end = bookmark.end ? K(bookmark.end, doc) : null; if (!start || start.length < 1) { return self; } self.setStartBefore(start[0]); start.remove(); if (end && end.length > 0) { self.setEndBefore(end[0]); end.remove(); } else { self.collapse(true); } return self; }, dump : function() { console.log('--------------------'); console.log(this.startContainer.nodeType == 3 ? this.startContainer.nodeValue : this.startContainer, this.startOffset); console.log(this.endContainer.nodeType == 3 ? this.endContainer.nodeValue : this.endContainer, this.endOffset); } }); function _range(mixed) { if (!mixed.nodeName) { return mixed.constructor === KRange ? mixed : _toRange(mixed); } return new KRange(mixed); } K.RangeClass = KRange; K.range = _range; K.START_TO_START = _START_TO_START; K.START_TO_END = _START_TO_END; K.END_TO_END = _END_TO_END; K.END_TO_START = _END_TO_START;