if (!dojo._hasResource["dijit._editor.range"]) { // _hasResource checks added // by build. Do not use // _hasResource directly in // your code. dojo._hasResource["dijit._editor.range"] = true; dojo.provide("dijit._editor.range"); dijit.range = {}; dijit.range.getIndex = function(/* DomNode */node, /* DomNode */parent) { // dojo.profile.start("dijit.range.getIndex"); var ret = [], retR = []; var stop = parent; var onode = node; while (node != stop) { var i = 0; var pnode = node.parentNode, n; while (n = pnode.childNodes[i++]) { if (n === node) { --i; break; } } if (i >= pnode.childNodes.length) { dojo .debug("Error finding index of a node in dijit.range.getIndex"); } ret.unshift(i); retR.unshift(i - pnode.childNodes.length); node = pnode; } // normalized() can not be called so often to prevent // invalidating selection/range, so we have to detect // here that any text nodes in a row if (ret.length > 0 && onode.nodeType == 3) { var n = onode.previousSibling; while (n && n.nodeType == 3) { ret[ret.length - 1]--; n = n.previousSibling; } n = onode.nextSibling; while (n && n.nodeType == 3) { retR[retR.length - 1]++; n = n.nextSibling; } } // dojo.profile.end("dijit.range.getIndex"); return { o : ret, r : retR }; } dijit.range.getNode = function(/* Array */index, /* DomNode */parent) { if (!dojo.isArray(index) || index.length == 0) { return parent; } var node = parent; // if(!node)debugger dojo.every(index, function(i) { if (i >= 0 && i < node.childNodes.length) { node = node.childNodes[i]; } else { node = null; console.debug('Error: can not find node with index', index, 'under parent node', parent); return false; // terminate dojo.every } return true; // carry on the every loop }); return node; } dijit.range.getCommonAncestor = function(n1, n2, root) { var getAncestors = function(n, root) { var as = []; while (n) { as.unshift(n); if (n != root && n.tagName != 'BODY') { n = n.parentNode; } else { break; } } return as; }; var n1as = getAncestors(n1, root); var n2as = getAncestors(n2, root); var m = Math.min(n1as.length, n2as.length); var com = n1as[0]; // at least, one element should be in the array: the // root (BODY by default) for (var i = 1; i < m; i++) { if (n1as[i] === n2as[i]) { com = n1as[i] } else { break; } } return com; } dijit.range.getAncestor = function(/* DomNode */node, /* RegEx? */regex, /* DomNode? */ root) { root = root || node.ownerDocument.body; while (node && node !== root) { var name = node.nodeName.toUpperCase(); if (regex.test(name)) { return node; } node = node.parentNode; } return null; } dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/; dijit.range.getBlockAncestor = function(/* DomNode */node, /* RegEx? */regex, /* DomNode? */ root) { root = root || node.ownerDocument.body; regex = regex || dijit.range.BlockTagNames; var block = null, blockContainer; while (node && node !== root) { var name = node.nodeName.toUpperCase(); if (!block && regex.test(name)) { block = node; } if (!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)) { blockContainer = node; } node = node.parentNode; } return { blockNode : block, blockContainer : blockContainer || node.ownerDocument.body }; } dijit.range.atBeginningOfContainer = function(/* DomNode */container, /* DomNode */ node, /* Int */offset) { var atBeginning = false; var offsetAtBeginning = (offset == 0); if (!offsetAtBeginning && node.nodeType == 3) { // if this is a text // node, check whether // the left part is all // space if (dojo.trim(node.nodeValue.substr(0, offset)) == 0) { offsetAtBeginning = true; } } if (offsetAtBeginning) { var cnode = node; atBeginning = true; while (cnode && cnode !== container) { if (cnode.previousSibling) { atBeginning = false; break; } cnode = cnode.parentNode; } } return atBeginning; } dijit.range.atEndOfContainer = function(/* DomNode */container, /* DomNode */ node, /* Int */offset) { var atEnd = false; var offsetAtEnd = (offset == (node.length || node.childNodes.length)); if (!offsetAtEnd && node.nodeType == 3) { // if this is a text node, // check whether the right // part is all space if (dojo.trim(node.nodeValue.substr(offset)) == 0) { offsetAtEnd = true; } } if (offsetAtEnd) { var cnode = node; atEnd = true; while (cnode && cnode !== container) { if (cnode.nextSibling) { atEnd = false; break; } cnode = cnode.parentNode; } } return atEnd; } dijit.range.adjacentNoneTextNode = function(startnode, next) { var node = startnode; var len = (0 - startnode.length) || 0; var prop = next ? 'nextSibling' : 'previousSibling'; while (node) { if (node.nodeType != 3) { break; } len += node.length node = node[prop]; } return [node, len]; } dijit.range._w3c = Boolean(window['getSelection']); dijit.range.create = function() { if (dijit.range._w3c) { return document.createRange(); } else {// IE return new dijit.range.W3CRange; } } dijit.range.getSelection = function(win, /* Boolean? */ignoreUpdate) { if (dijit.range._w3c) { return win.getSelection(); } else {// IE var id = win.__W3CRange; if (!id || !dijit.range.ie.cachedSelection[id]) { var s = new dijit.range.ie.selection(win); // use win as the key in an object is not reliable, which // can leads to quite odd behaviors. thus we generate a // string and use it as a key in the cache id = (new Date).getTime(); while (id in dijit.range.ie.cachedSelection) { id = id + 1; } id = String(id); dijit.range.ie.cachedSelection[id] = s; } else { var s = dijit.range.ie.cachedSelection[id]; } if (!ignoreUpdate) { s._getCurrentSelection(); } return s; } } if (!dijit.range._w3c) { dijit.range.ie = { cachedSelection : {}, selection : function(win) { this._ranges = []; this.addRange = function(r, /* boolean */internal) { this._ranges.push(r); if (!internal) { r._select(); } this.rangeCount = this._ranges.length; }; this.removeAllRanges = function() { // don't detach, the range may be used later // for(var i=0;i 0) { dojo.every(parentNode.childNodes, function(node, i) { var calOffset; if (node.nodeType != 3) { atmrange.moveToElementText(node); if (atmrange .compareEndPoints(cmpstr, range) > 0) { startnode = node.previousSibling; if (lastNode && lastNode.nodeType == 3) { // where share we put the start? in // the text node or after? startnode = lastNode; calOffset = true; } else { startnode = parentNode; startOffset = i; return false; } } else { if (i == parentNode.childNodes.length - 1) { startnode = parentNode; startOffset = parentNode.childNodes.length; return false; } } } else { if (i == parentNode.childNodes.length - 1) {// at // the // end // of // this // node startnode = node; calOffset = true; } } // try{ if (calOffset && startnode) { var prevnode = dijit.range .adjacentNoneTextNode(startnode)[0]; if (prevnode) { startnode = prevnode.nextSibling; } else { startnode = parentNode.firstChild; // firstChild // must // be a // text // node } var prevnodeobj = dijit.range .adjacentNoneTextNode(startnode); prevnode = prevnodeobj[0]; var lenoffset = prevnodeobj[1]; if (prevnode) { atmrange.moveToElementText(prevnode); atmrange.collapse(false); } else { atmrange.moveToElementText(parentNode); } atmrange.setEndPoint(cmpstr, range); startOffset = atmrange.text.length - lenoffset; return false; } // }catch(e){ debugger } lastNode = node; return true; }); } else { startnode = parentNode; startOffset = 0; } // if at the end of startnode and we are dealing with start // container, then // move the startnode to nextSibling if it is a text node // TODO: do this for end container? if (!end && startnode.nodeType != 3 && startOffset == startnode.childNodes.length) { if (startnode.nextSibling && startnode.nextSibling.nodeType == 3) { startnode = startnode.nextSibling; startOffset = 0; } } return [startnode, startOffset]; }, setEndPoint : function(range, container, offset) { // text node var atmrange = range.duplicate(); if (container.nodeType != 3) { // normal node atmrange.moveToElementText(container); atmrange.collapse(true); if (offset == container.childNodes.length) { if (offset > 0) { // a simple atmrange.collapse(false); won't work // here: // although moveToElementText(node) is supposed to // encompass the content of the node, // but when collapse to end, it is in fact after the // ending tag of node (collapse to start // is after the begining tag of node as expected) var node = container.lastChild; var len = 0; while (node && node.nodeType == 3) { len += node.length; container = node; // pass through node = node.previousSibling; } if (node) { atmrange.moveToElementText(node); } atmrange.collapse(false); offset = len; // pass through } else { // no childNodes atmrange.moveToElementText(container); atmrange.collapse(true); } } else { if (offset > 0) { var node = container.childNodes[offset - 1]; if (node.nodeType == 3) { container = node; offset = node.length; // pass through } else { atmrange.moveToElementText(node); atmrange.collapse(false); } } } } if (container.nodeType == 3) { var prevnodeobj = dijit.range .adjacentNoneTextNode(container); var prevnode = prevnodeobj[0], len = prevnodeobj[1]; if (prevnode) { atmrange.moveToElementText(prevnode); atmrange.collapse(false); // if contentEditable is not inherit, the above collapse // won't make the end point // in the correctly position: it always has a -1 offset, // so compensate it if (prevnode.contentEditable != 'inherit') { len++; } } else { atmrange.moveToElementText(container.parentNode); atmrange.collapse(true); } offset += len; if (offset > 0) { if (atmrange.moveEnd('character', offset) != offset) { alert('Error when moving!'); } atmrange.collapse(false); } } return atmrange; }, decomposeTextRange : function(range) { var tmpary = dijit.range.ie.getEndPoint(range); var startContainter = tmpary[0], startOffset = tmpary[1]; var endContainter = tmpary[0], endOffset = tmpary[1]; if (range.htmlText.length) { if (range.htmlText == range.text) { // in the same text node endOffset = startOffset + range.text.length; } else { tmpary = dijit.range.ie.getEndPoint(range, true); endContainter = tmpary[0], endOffset = tmpary[1]; } } return [[startContainter, startOffset], [endContainter, endOffset], range.parentElement()]; }, setRange : function(range, startContainter, startOffset, endContainter, endOffset, check) { var startrange = dijit.range.ie.setEndPoint(range, startContainter, startOffset); range.setEndPoint('StartToStart', startrange); if (!this.collapsed) { var endrange = dijit.range.ie.setEndPoint(range, endContainter, endOffset); range.setEndPoint('EndToEnd', endrange); } return range; } } dojo.declare("dijit.range.W3CRange", null, { constructor : function() { if (arguments.length > 0) { this.setStart(arguments[0][0][0], arguments[0][0][1]); this.setEnd(arguments[0][1][0], arguments[0][1][1], arguments[0][2]); } else { this.commonAncestorContainer = null; this.startContainer = null; this.startOffset = 0; this.endContainer = null; this.endOffset = 0; this.collapsed = true; } }, _simpleSetEndPoint : function(node, range, end) { var r = (this._body || node.ownerDocument.body) .createTextRange(); if (node.nodeType != 1) { r.moveToElementText(node.parentNode); } else { r.moveToElementText(node); } r.collapse(true); range.setEndPoint(end ? 'EndToEnd' : 'StartToStart', r); }, _updateInternal : function(__internal_common) { if (this.startContainer !== this.endContainer) { if (!__internal_common) { var r = (this._body || this.startContainer.ownerDocument.body) .createTextRange(); this._simpleSetEndPoint(this.startContainer, r); this._simpleSetEndPoint(this.endContainer, r, true); __internal_common = r.parentElement(); } this.commonAncestorContainer = dijit.range .getCommonAncestor(this.startContainer, this.endContainer, __internal_common); } else { this.commonAncestorContainer = this.startContainer; } this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset); }, setStart : function(node, offset, __internal_common) { if (this.startContainer === node && this.startOffset == offset) { return; } delete this._cachedBookmark; this.startContainer = node; this.startOffset = offset; if (!this.endContainer) { this.setEnd(node, offset, __internal_common); } else { this._updateInternal(__internal_common); } }, setEnd : function(node, offset, __internal_common) { if (this.endContainer === node && this.endOffset == offset) { return; } delete this._cachedBookmark; this.endContainer = node; this.endOffset = offset; if (!this.startContainer) { this.setStart(node, offset, __internal_common); } else { this._updateInternal(__internal_common); } }, setStartAfter : function(node, offset) { this._setPoint('setStart', node, offset, 1); }, setStartBefore : function(node, offset) { this._setPoint('setStart', node, offset, 0); }, setEndAfter : function(node, offset) { this._setPoint('setEnd', node, offset, 1); }, setEndBefore : function(node, offset) { this._setPoint('setEnd', node, offset, 0); }, _setPoint : function(what, node, offset, ext) { var index = dijit.range.getIndex(node, node.parentNode).o; this[what](node.parentNode, index.pop() + ext); }, _getIERange : function() { var r = (this._body || this.endContainer.ownerDocument.body) .createTextRange(); dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset); return r; }, getBookmark : function(body) { this._getIERange(); return this._cachedBookmark; }, _select : function() { var r = this._getIERange(); r.select(); }, deleteContents : function() { var r = this._getIERange(); r.pasteHTML(''); this.endContainer = this.startContainer; this.endOffset = this.startOffset; this.collapsed = true; }, cloneRange : function() { var r = new dijit.range.W3CRange([ [this.startContainer, this.startOffset], [this.endContainer, this.endOffset]]); r._body = this._body; return r; }, detach : function() { this._body = null; this.commonAncestorContainer = null; this.startContainer = null; this.startOffset = 0; this.endContainer = null; this.endOffset = 0; this.collapsed = true; } }); } // if(!dijit.range._w3c) }