]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_editor/range.js.uncompressed.js
1 define("dijit/_editor/range", [
2 "dojo/_base/array", // array.every
3 "dojo/_base/declare", // declare
4 "dojo/_base/lang", // lang.isArray
5 "dojo/_base/window", // win.global
6 ".." // for exporting symbols to dijit, TODO: remove in 2.0
7 ], function(array
, declare
, lang
, win
, dijit
){
10 // dijit/_editor/range
17 dijit
.range
.getIndex = function(/*DomNode*/node
, /*DomNode*/parent
){
18 // dojo.profile.start("dijit.range.getIndex");
19 var ret
= [], retR
= [];
23 while(node
!= parent
){
25 pnode
= node
.parentNode
;
26 while((n
= pnode
.childNodes
[i
++])){
32 //if(i>=pnode.childNodes.length){
33 //dojo.debug("Error finding index of a node in dijit.range.getIndex");
36 retR
.unshift(i
- pnode
.childNodes
.length
);
40 //normalized() can not be called so often to prevent
41 //invalidating selection/range, so we have to detect
42 //here that any text nodes in a row
43 if(ret
.length
> 0 && onode
.nodeType
== 3){
44 n
= onode
.previousSibling
;
45 while(n
&& n
.nodeType
== 3){
46 ret
[ret
.length
- 1]--;
47 n
= n
.previousSibling
;
49 n
= onode
.nextSibling
;
50 while(n
&& n
.nodeType
== 3){
51 retR
[retR
.length
- 1]++;
55 // dojo.profile.end("dijit.range.getIndex");
56 return {o
: ret
, r
:retR
};
59 dijit
.range
.getNode = function(/*Array*/index
, /*DomNode*/parent
){
60 if(!lang
.isArray(index
) || index
.length
== 0){
65 array
.every(index
, function(i
){
66 if(i
>= 0 && i
< node
.childNodes
.length
){
67 node
= node
.childNodes
[i
];
70 //console.debug('Error: can not find node with index',index,'under parent node',parent );
71 return false; //terminate array.every
73 return true; //carry on the every loop
79 dijit
.range
.getCommonAncestor = function(n1
, n2
, root
){
80 root
= root
|| n1
.ownerDocument
.body
;
81 var getAncestors = function(n
){
93 var n1as
= getAncestors(n1
);
94 var n2as
= getAncestors(n2
);
96 var m
= Math
.min(n1as
.length
, n2as
.length
);
97 var com
= n1as
[0]; //at least, one element should be in the array: the root (BODY by default)
98 for(var i
= 1; i
< m
; i
++){
99 if(n1as
[i
] === n2as
[i
]){
108 dijit
.range
.getAncestor = function(/*DomNode*/node
, /*RegEx?*/regex
, /*DomNode?*/root
){
109 root
= root
|| node
.ownerDocument
.body
;
110 while(node
&& node
!== root
){
111 var name
= node
.nodeName
.toUpperCase();
112 if(regex
.test(name
)){
116 node
= node
.parentNode
;
121 dijit
.range
.BlockTagNames
= /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
122 dijit
.range
.getBlockAncestor = function(/*DomNode*/node
, /*RegEx?*/regex
, /*DomNode?*/root
){
123 root
= root
|| node
.ownerDocument
.body
;
124 regex
= regex
|| dijit
.range
.BlockTagNames
;
125 var block
= null, blockContainer
;
126 while(node
&& node
!== root
){
127 var name
= node
.nodeName
.toUpperCase();
128 if(!block
&& regex
.test(name
)){
131 if(!blockContainer
&& (/^(?:BODY|TD|TH|CAPTION)$/).test(name
)){
132 blockContainer
= node
;
135 node
= node
.parentNode
;
137 return {blockNode
:block
, blockContainer
:blockContainer
|| node
.ownerDocument
.body
};
140 dijit
.range
.atBeginningOfContainer = function(/*DomNode*/container
, /*DomNode*/node
, /*Int*/offset
){
141 var atBeginning
= false;
142 var offsetAtBeginning
= (offset
== 0);
143 if(!offsetAtBeginning
&& node
.nodeType
== 3){ //if this is a text node, check whether the left part is all space
144 if(/^[\s\xA0]+$/.test(node
.nodeValue
.substr(0, offset
))){
145 offsetAtBeginning
= true;
148 if(offsetAtBeginning
){
151 while(cnode
&& cnode
!== container
){
152 if(cnode
.previousSibling
){
156 cnode
= cnode
.parentNode
;
162 dijit
.range
.atEndOfContainer = function(/*DomNode*/container
, /*DomNode*/node
, /*Int*/offset
){
164 var offsetAtEnd
= (offset
== (node
.length
|| node
.childNodes
.length
));
165 if(!offsetAtEnd
&& node
.nodeType
== 3){ //if this is a text node, check whether the right part is all space
166 if(/^[\s\xA0]+$/.test(node
.nodeValue
.substr(offset
))){
173 while(cnode
&& cnode
!== container
){
174 if(cnode
.nextSibling
){
178 cnode
= cnode
.parentNode
;
184 dijit
.range
.adjacentNoneTextNode = function(startnode
, next
){
185 var node
= startnode
;
186 var len
= (0 - startnode
.length
) || 0;
187 var prop
= next
? 'nextSibling' : 'previousSibling';
189 if(node
.nodeType
!= 3){
198 dijit
.range
._w3c
= Boolean(window
['getSelection']);
199 dijit
.range
.create = function(/*Window?*/window
){
200 if(dijit
.range
._w3c
){
201 return (window
|| win
.global
).document
.createRange();
203 return new dijit
.range
.W3CRange
;
207 dijit
.range
.getSelection = function(/*Window*/win
, /*Boolean?*/ignoreUpdate
){
208 if(dijit
.range
._w3c
){
209 return win
.getSelection();
211 var s
= new dijit
.range
.ie
.selection(win
);
213 s
._getCurrentSelection();
219 if(!dijit
.range
._w3c
){
222 selection: function(win
){
224 this.addRange = function(r
, /*boolean*/internal){
225 this._ranges
.push(r
);
229 this.rangeCount
= this._ranges
.length
;
231 this.removeAllRanges = function(){
232 //don't detach, the range may be used later
233 // for(var i=0;i<this._ranges.length;i++){
234 // this._ranges[i].detach();
239 var _initCurrentRange = function(){
240 var r
= win
.document
.selection
.createRange();
241 var type
= win
.document
.selection
.type
.toUpperCase();
242 if(type
== "CONTROL"){
243 //TODO: multiple range selection(?)
244 return new dijit
.range
.W3CRange(dijit
.range
.ie
.decomposeControlRange(r
));
246 return new dijit
.range
.W3CRange(dijit
.range
.ie
.decomposeTextRange(r
));
249 this.getRangeAt = function(i
){
250 return this._ranges
[i
];
252 this._getCurrentSelection = function(){
253 this.removeAllRanges();
254 var r
= _initCurrentRange();
256 this.addRange(r
, true);
257 this.isCollapsed
= r
.collapsed
;
259 this.isCollapsed
= true;
263 decomposeControlRange: function(range
){
264 var firstnode
= range
.item(0), lastnode
= range
.item(range
.length
- 1);
265 var startContainer
= firstnode
.parentNode
, endContainer
= lastnode
.parentNode
;
266 var startOffset
= dijit
.range
.getIndex(firstnode
, startContainer
).o
[0];
267 var endOffset
= dijit
.range
.getIndex(lastnode
, endContainer
).o
[0] + 1;
268 return [startContainer
, startOffset
,endContainer
, endOffset
];
270 getEndPoint: function(range
, end
){
271 var atmrange
= range
.duplicate();
272 atmrange
.collapse(!end
);
273 var cmpstr
= 'EndTo' + (end
? 'End' : 'Start');
274 var parentNode
= atmrange
.parentElement();
276 var startnode
, startOffset
, lastNode
;
277 if(parentNode
.childNodes
.length
> 0){
278 array
.every(parentNode
.childNodes
, function(node
, i
){
280 if(node
.nodeType
!= 3){
281 atmrange
.moveToElementText(node
);
283 if(atmrange
.compareEndPoints(cmpstr
, range
) > 0){
284 //startnode = node.previousSibling;
285 if(lastNode
&& lastNode
.nodeType
== 3){
286 //where shall we put the start? in the text node or after?
287 startnode
= lastNode
;
290 startnode
= parentNode
;
295 if(i
== parentNode
.childNodes
.length
- 1){
296 startnode
= parentNode
;
297 startOffset
= parentNode
.childNodes
.length
;
302 if(i
== parentNode
.childNodes
.length
- 1){//at the end of this node
308 if(calOffset
&& startnode
){
309 var prevnode
= dijit
.range
.adjacentNoneTextNode(startnode
)[0];
311 startnode
= prevnode
.nextSibling
;
313 startnode
= parentNode
.firstChild
; //firstChild must be a text node
315 var prevnodeobj
= dijit
.range
.adjacentNoneTextNode(startnode
);
316 prevnode
= prevnodeobj
[0];
317 var lenoffset
= prevnodeobj
[1];
319 atmrange
.moveToElementText(prevnode
);
320 atmrange
.collapse(false);
322 atmrange
.moveToElementText(parentNode
);
324 atmrange
.setEndPoint(cmpstr
, range
);
325 startOffset
= atmrange
.text
.length
- lenoffset
;
329 // }catch(e){ debugger }
334 startnode
= parentNode
;
338 //if at the end of startnode and we are dealing with start container, then
339 //move the startnode to nextSibling if it is a text node
340 //TODO: do this for end container?
341 if(!end
&& startnode
.nodeType
== 1 && startOffset
== startnode
.childNodes
.length
){
342 var nextnode
= startnode
.nextSibling
;
343 if(nextnode
&& nextnode
.nodeType
== 3){
344 startnode
= nextnode
;
348 return [startnode
, startOffset
];
350 setEndPoint: function(range
, container
, offset
){
352 var atmrange
= range
.duplicate(), node
, len
;
353 if(container
.nodeType
!= 3){ //normal node
355 node
= container
.childNodes
[offset
- 1];
357 if(node
.nodeType
== 3){
359 offset
= node
.length
;
362 if(node
.nextSibling
&& node
.nextSibling
.nodeType
== 3){
363 container
= node
.nextSibling
;
367 atmrange
.moveToElementText(node
.nextSibling
? node
: container
);
368 var parent
= node
.parentNode
;
369 var tempNode
= parent
.insertBefore(node
.ownerDocument
.createTextNode(' '), node
.nextSibling
);
370 atmrange
.collapse(false);
371 parent
.removeChild(tempNode
);
376 atmrange
.moveToElementText(container
);
377 atmrange
.collapse(true);
380 if(container
.nodeType
== 3){
381 var prevnodeobj
= dijit
.range
.adjacentNoneTextNode(container
);
382 var prevnode
= prevnodeobj
[0];
383 len
= prevnodeobj
[1];
385 atmrange
.moveToElementText(prevnode
);
386 atmrange
.collapse(false);
387 //if contentEditable is not inherit, the above collapse won't make the end point
388 //in the correctly position: it always has a -1 offset, so compensate it
389 if(prevnode
.contentEditable
!= 'inherit'){
393 atmrange
.moveToElementText(container
.parentNode
);
394 atmrange
.collapse(true);
399 if(atmrange
.move('character', offset
) != offset
){
400 console
.error('Error when moving!');
407 decomposeTextRange: function(range
){
408 var tmpary
= dijit
.range
.ie
.getEndPoint(range
);
409 var startContainer
= tmpary
[0], startOffset
= tmpary
[1];
410 var endContainer
= tmpary
[0], endOffset
= tmpary
[1];
412 if(range
.htmlText
.length
){
413 if(range
.htmlText
== range
.text
){ //in the same text node
414 endOffset
= startOffset
+ range
.text
.length
;
416 tmpary
= dijit
.range
.ie
.getEndPoint(range
, true);
417 endContainer
= tmpary
[0],endOffset
= tmpary
[1];
418 // if(startContainer.tagName == "BODY"){
419 // startContainer = startContainer.firstChild;
423 return [startContainer
, startOffset
, endContainer
, endOffset
];
425 setRange: function(range
, startContainer
, startOffset
, endContainer
, endOffset
, collapsed
){
426 var start
= dijit
.range
.ie
.setEndPoint(range
, startContainer
, startOffset
);
428 range
.setEndPoint('StartToStart', start
);
430 var end
= dijit
.range
.ie
.setEndPoint(range
, endContainer
, endOffset
);
432 range
.setEndPoint('EndToEnd', end
|| start
);
438 declare("dijit.range.W3CRange",null, {
439 constructor: function(){
440 if(arguments
.length
>0){
441 this.setStart(arguments
[0][0],arguments
[0][1]);
442 this.setEnd(arguments
[0][2],arguments
[0][3]);
444 this.commonAncestorContainer
= null;
445 this.startContainer
= null;
446 this.startOffset
= 0;
447 this.endContainer
= null;
449 this.collapsed
= true;
452 _updateInternal: function(){
453 if(this.startContainer
!== this.endContainer
){
454 this.commonAncestorContainer
= dijit
.range
.getCommonAncestor(this.startContainer
, this.endContainer
);
456 this.commonAncestorContainer
= this.startContainer
;
458 this.collapsed
= (this.startContainer
=== this.endContainer
) && (this.startOffset
== this.endOffset
);
460 setStart: function(node
, offset
){
461 offset
=parseInt(offset
);
462 if(this.startContainer
=== node
&& this.startOffset
== offset
){
465 delete this._cachedBookmark
;
467 this.startContainer
= node
;
468 this.startOffset
= offset
;
469 if(!this.endContainer
){
470 this.setEnd(node
, offset
);
472 this._updateInternal();
475 setEnd: function(node
, offset
){
476 offset
=parseInt(offset
);
477 if(this.endContainer
=== node
&& this.endOffset
== offset
){
480 delete this._cachedBookmark
;
482 this.endContainer
= node
;
483 this.endOffset
= offset
;
484 if(!this.startContainer
){
485 this.setStart(node
, offset
);
487 this._updateInternal();
490 setStartAfter: function(node
, offset
){
491 this._setPoint('setStart', node
, offset
, 1);
493 setStartBefore: function(node
, offset
){
494 this._setPoint('setStart', node
, offset
, 0);
496 setEndAfter: function(node
, offset
){
497 this._setPoint('setEnd', node
, offset
, 1);
499 setEndBefore: function(node
, offset
){
500 this._setPoint('setEnd', node
, offset
, 0);
502 _setPoint: function(what
, node
, offset
, ext
){
503 var index
= dijit
.range
.getIndex(node
, node
.parentNode
).o
;
504 this[what
](node
.parentNode
, index
.pop()+ext
);
506 _getIERange: function(){
507 var r
= (this._body
|| this.endContainer
.ownerDocument
.body
).createTextRange();
508 dijit
.range
.ie
.setRange(r
, this.startContainer
, this.startOffset
, this.endContainer
, this.endOffset
, this.collapsed
);
511 getBookmark: function(){
513 return this._cachedBookmark
;
516 var r
= this._getIERange();
519 deleteContents: function(){
520 var s
= this.startContainer
, r
= this._getIERange();
521 if(s
.nodeType
=== 3 && !this.startOffset
){
522 //if the range starts at the beginning of a
523 //text node, move it to before the textnode
524 //to make sure the range is still valid
525 //after deleteContents() finishes
526 this.setStartBefore(s
);
529 this.endContainer
= this.startContainer
;
530 this.endOffset
= this.startOffset
;
531 this.collapsed
= true;
533 cloneRange: function(){
534 var r
= new dijit
.range
.W3CRange([this.startContainer
,this.startOffset
,
535 this.endContainer
,this.endOffset
]);
536 r
._body
= this._body
;
541 this.commonAncestorContainer
= null;
542 this.startContainer
= null;
543 this.startOffset
= 0;
544 this.endContainer
= null;
546 this.collapsed
= true;
549 } //if(!dijit.range._w3c)