]> git.pond.sub.org Git - eow/blobdiff - static/dojo-release-1.1.1/dijit/_editor/range.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dijit / _editor / range.js
diff --git a/static/dojo-release-1.1.1/dijit/_editor/range.js b/static/dojo-release-1.1.1/dijit/_editor/range.js
new file mode 100644 (file)
index 0000000..cd84b60
--- /dev/null
@@ -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)
+
+}