]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/_editor/range.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / 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){
8
9 // module:
10 // dijit/_editor/range
11 // summary:
12 // W3C range API
13
14
15 dijit.range={};
16
17 dijit.range.getIndex = function(/*DomNode*/node, /*DomNode*/parent){
18 // dojo.profile.start("dijit.range.getIndex");
19 var ret = [], retR = [];
20 var onode = node;
21
22 var pnode, n;
23 while(node != parent){
24 var i = 0;
25 pnode = node.parentNode;
26 while((n = pnode.childNodes[i++])){
27 if(n === node){
28 --i;
29 break;
30 }
31 }
32 //if(i>=pnode.childNodes.length){
33 //dojo.debug("Error finding index of a node in dijit.range.getIndex");
34 //}
35 ret.unshift(i);
36 retR.unshift(i - pnode.childNodes.length);
37 node = pnode;
38 }
39
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;
48 }
49 n = onode.nextSibling;
50 while(n && n.nodeType == 3){
51 retR[retR.length - 1]++;
52 n = n.nextSibling;
53 }
54 }
55 // dojo.profile.end("dijit.range.getIndex");
56 return {o: ret, r:retR};
57 };
58
59 dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){
60 if(!lang.isArray(index) || index.length == 0){
61 return parent;
62 }
63 var node = parent;
64 // if(!node)debugger
65 array.every(index, function(i){
66 if(i >= 0 && i < node.childNodes.length){
67 node = node.childNodes[i];
68 }else{
69 node = null;
70 //console.debug('Error: can not find node with index',index,'under parent node',parent );
71 return false; //terminate array.every
72 }
73 return true; //carry on the every loop
74 });
75
76 return node;
77 };
78
79 dijit.range.getCommonAncestor = function(n1, n2, root){
80 root = root || n1.ownerDocument.body;
81 var getAncestors = function(n){
82 var as = [];
83 while(n){
84 as.unshift(n);
85 if(n !== root){
86 n = n.parentNode;
87 }else{
88 break;
89 }
90 }
91 return as;
92 };
93 var n1as = getAncestors(n1);
94 var n2as = getAncestors(n2);
95
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]){
100 com = n1as[i]
101 }else{
102 break;
103 }
104 }
105 return com;
106 };
107
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)){
113 return node;
114 }
115
116 node = node.parentNode;
117 }
118 return null;
119 };
120
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)){
129 block = node;
130 }
131 if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){
132 blockContainer = node;
133 }
134
135 node = node.parentNode;
136 }
137 return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body};
138 };
139
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;
146 }
147 }
148 if(offsetAtBeginning){
149 var cnode = node;
150 atBeginning = true;
151 while(cnode && cnode !== container){
152 if(cnode.previousSibling){
153 atBeginning = false;
154 break;
155 }
156 cnode = cnode.parentNode;
157 }
158 }
159 return atBeginning;
160 };
161
162 dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
163 var atEnd = false;
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))){
167 offsetAtEnd = true;
168 }
169 }
170 if(offsetAtEnd){
171 var cnode = node;
172 atEnd = true;
173 while(cnode && cnode !== container){
174 if(cnode.nextSibling){
175 atEnd = false;
176 break;
177 }
178 cnode = cnode.parentNode;
179 }
180 }
181 return atEnd;
182 };
183
184 dijit.range.adjacentNoneTextNode = function(startnode, next){
185 var node = startnode;
186 var len = (0 - startnode.length) || 0;
187 var prop = next ? 'nextSibling' : 'previousSibling';
188 while(node){
189 if(node.nodeType != 3){
190 break;
191 }
192 len += node.length;
193 node = node[prop];
194 }
195 return [node,len];
196 };
197
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();
202 }else{//IE
203 return new dijit.range.W3CRange;
204 }
205 };
206
207 dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){
208 if(dijit.range._w3c){
209 return win.getSelection();
210 }else{//IE
211 var s = new dijit.range.ie.selection(win);
212 if(!ignoreUpdate){
213 s._getCurrentSelection();
214 }
215 return s;
216 }
217 };
218
219 if(!dijit.range._w3c){
220 dijit.range.ie = {
221 cachedSelection: {},
222 selection: function(win){
223 this._ranges = [];
224 this.addRange = function(r, /*boolean*/internal){
225 this._ranges.push(r);
226 if(!internal){
227 r._select();
228 }
229 this.rangeCount = this._ranges.length;
230 };
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();
235 // }
236 this._ranges = [];
237 this.rangeCount = 0;
238 };
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));
245 }else{
246 return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
247 }
248 };
249 this.getRangeAt = function(i){
250 return this._ranges[i];
251 };
252 this._getCurrentSelection = function(){
253 this.removeAllRanges();
254 var r = _initCurrentRange();
255 if(r){
256 this.addRange(r, true);
257 this.isCollapsed = r.collapsed;
258 }else{
259 this.isCollapsed = true;
260 }
261 };
262 },
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];
269 },
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();
275
276 var startnode, startOffset, lastNode;
277 if(parentNode.childNodes.length > 0){
278 array.every(parentNode.childNodes, function(node, i){
279 var calOffset;
280 if(node.nodeType != 3){
281 atmrange.moveToElementText(node);
282
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;
288 calOffset = true;
289 }else{
290 startnode = parentNode;
291 startOffset = i;
292 return false;
293 }
294 }else{
295 if(i == parentNode.childNodes.length - 1){
296 startnode = parentNode;
297 startOffset = parentNode.childNodes.length;
298 return false;
299 }
300 }
301 }else{
302 if(i == parentNode.childNodes.length - 1){//at the end of this node
303 startnode = node;
304 calOffset = true;
305 }
306 }
307 // try{
308 if(calOffset && startnode){
309 var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
310 if(prevnode){
311 startnode = prevnode.nextSibling;
312 }else{
313 startnode = parentNode.firstChild; //firstChild must be a text node
314 }
315 var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
316 prevnode = prevnodeobj[0];
317 var lenoffset = prevnodeobj[1];
318 if(prevnode){
319 atmrange.moveToElementText(prevnode);
320 atmrange.collapse(false);
321 }else{
322 atmrange.moveToElementText(parentNode);
323 }
324 atmrange.setEndPoint(cmpstr, range);
325 startOffset = atmrange.text.length - lenoffset;
326
327 return false;
328 }
329 // }catch(e){ debugger }
330 lastNode = node;
331 return true;
332 });
333 }else{
334 startnode = parentNode;
335 startOffset = 0;
336 }
337
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;
345 startOffset = 0;
346 }
347 }
348 return [startnode, startOffset];
349 },
350 setEndPoint: function(range, container, offset){
351 //text node
352 var atmrange = range.duplicate(), node, len;
353 if(container.nodeType != 3){ //normal node
354 if(offset > 0){
355 node = container.childNodes[offset - 1];
356 if(node){
357 if(node.nodeType == 3){
358 container = node;
359 offset = node.length;
360 //pass through
361 }else{
362 if(node.nextSibling && node.nextSibling.nodeType == 3){
363 container = node.nextSibling;
364 offset = 0;
365 //pass through
366 }else{
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);
372 }
373 }
374 }
375 }else{
376 atmrange.moveToElementText(container);
377 atmrange.collapse(true);
378 }
379 }
380 if(container.nodeType == 3){
381 var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
382 var prevnode = prevnodeobj[0];
383 len = prevnodeobj[1];
384 if(prevnode){
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'){
390 len++;
391 }
392 }else{
393 atmrange.moveToElementText(container.parentNode);
394 atmrange.collapse(true);
395 }
396
397 offset += len;
398 if(offset > 0){
399 if(atmrange.move('character', offset) != offset){
400 console.error('Error when moving!');
401 }
402 }
403 }
404
405 return atmrange;
406 },
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];
411
412 if(range.htmlText.length){
413 if(range.htmlText == range.text){ //in the same text node
414 endOffset = startOffset + range.text.length;
415 }else{
416 tmpary = dijit.range.ie.getEndPoint(range, true);
417 endContainer = tmpary[0],endOffset = tmpary[1];
418 // if(startContainer.tagName == "BODY"){
419 // startContainer = startContainer.firstChild;
420 // }
421 }
422 }
423 return [startContainer, startOffset, endContainer, endOffset];
424 },
425 setRange: function(range, startContainer, startOffset, endContainer, endOffset, collapsed){
426 var start = dijit.range.ie.setEndPoint(range, startContainer, startOffset);
427
428 range.setEndPoint('StartToStart', start);
429 if(!collapsed){
430 var end = dijit.range.ie.setEndPoint(range, endContainer, endOffset);
431 }
432 range.setEndPoint('EndToEnd', end || start);
433
434 return range;
435 }
436 };
437
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]);
443 }else{
444 this.commonAncestorContainer = null;
445 this.startContainer = null;
446 this.startOffset = 0;
447 this.endContainer = null;
448 this.endOffset = 0;
449 this.collapsed = true;
450 }
451 },
452 _updateInternal: function(){
453 if(this.startContainer !== this.endContainer){
454 this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
455 }else{
456 this.commonAncestorContainer = this.startContainer;
457 }
458 this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
459 },
460 setStart: function(node, offset){
461 offset=parseInt(offset);
462 if(this.startContainer === node && this.startOffset == offset){
463 return;
464 }
465 delete this._cachedBookmark;
466
467 this.startContainer = node;
468 this.startOffset = offset;
469 if(!this.endContainer){
470 this.setEnd(node, offset);
471 }else{
472 this._updateInternal();
473 }
474 },
475 setEnd: function(node, offset){
476 offset=parseInt(offset);
477 if(this.endContainer === node && this.endOffset == offset){
478 return;
479 }
480 delete this._cachedBookmark;
481
482 this.endContainer = node;
483 this.endOffset = offset;
484 if(!this.startContainer){
485 this.setStart(node, offset);
486 }else{
487 this._updateInternal();
488 }
489 },
490 setStartAfter: function(node, offset){
491 this._setPoint('setStart', node, offset, 1);
492 },
493 setStartBefore: function(node, offset){
494 this._setPoint('setStart', node, offset, 0);
495 },
496 setEndAfter: function(node, offset){
497 this._setPoint('setEnd', node, offset, 1);
498 },
499 setEndBefore: function(node, offset){
500 this._setPoint('setEnd', node, offset, 0);
501 },
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);
505 },
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);
509 return r;
510 },
511 getBookmark: function(){
512 this._getIERange();
513 return this._cachedBookmark;
514 },
515 _select: function(){
516 var r = this._getIERange();
517 r.select();
518 },
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);
527 }
528 r.pasteHTML('');
529 this.endContainer = this.startContainer;
530 this.endOffset = this.startOffset;
531 this.collapsed = true;
532 },
533 cloneRange: function(){
534 var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
535 this.endContainer,this.endOffset]);
536 r._body = this._body;
537 return r;
538 },
539 detach: function(){
540 this._body = null;
541 this.commonAncestorContainer = null;
542 this.startContainer = null;
543 this.startOffset = 0;
544 this.endContainer = null;
545 this.endOffset = 0;
546 this.collapsed = true;
547 }
548 });
549 } //if(!dijit.range._w3c)
550
551
552 return dijit.range;
553 });