aboutsummaryrefslogtreecommitdiff
path: root/includes/js/dijit/_editor/range.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dijit/_editor/range.js')
-rw-r--r--includes/js/dijit/_editor/range.js570
1 files changed, 570 insertions, 0 deletions
diff --git a/includes/js/dijit/_editor/range.js b/includes/js/dijit/_editor/range.js
new file mode 100644
index 0000000..cd84b60
--- /dev/null
+++ b/includes/js/dijit/_editor/range.js
@@ -0,0 +1,570 @@
+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;
+
+ var pnode, n;
+ while(node != stop){
+ var i = 0;
+ pnode = node.parentNode;
+ 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){
+ 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 dojo.doc.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,s;
+ if(!id || !dijit.range.ie.cachedSelection[id]){
+ 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{
+ 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<this._ranges.length;i++){
+// this._ranges[i].detach();
+// }
+ this._ranges = [];
+ this.rangeCount = 0;
+ };
+ var _initCurrentRange = function(){
+ var r = win.document.selection.createRange();
+ var type=win.document.selection.type.toUpperCase();
+ if(type == "CONTROL"){
+ //TODO: multiple range selection(?)
+ return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
+ }else{
+ return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
+ }
+ };
+ this.getRangeAt = function(i){
+ return this._ranges[i];
+ };
+ this._getCurrentSelection = function(){
+ this.removeAllRanges();
+ var r=_initCurrentRange();
+ if(r){
+ this.addRange(r, true);
+ }
+ };
+ },
+ decomposeControlRange: function(range){
+ var firstnode = range.item(0), lastnode = range.item(range.length-1)
+ var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
+ var startOffset = dijit.range.getIndex(firstnode, startContainer).o;
+ var endOffset = dijit.range.getIndex(lastnode, endContainer).o+1;
+ return [[startContainer, startOffset],[endContainer, endOffset]];
+ },
+ getEndPoint: function(range, end){
+ var atmrange = range.duplicate();
+ atmrange.collapse(!end);
+ var cmpstr = 'EndTo' + (end?'End':'Start');
+ var parentNode = atmrange.parentElement();
+
+ var startnode, startOffset, lastNode;
+ if(parentNode.childNodes.length>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(), node, len;
+ 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)
+ node = container.lastChild;
+ 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){
+ 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){
+ offset=parseInt(offset);
+ 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){
+ offset=parseInt(offset);
+ 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)
+
+}