]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/_editor/range.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / _editor / range.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("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.doc TODO: remove in 2.0
6 "../main" // 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
15dijit.range={};
16
17dijit.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
59dijit.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
79dijit.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
108dijit.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
121dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
122dijit.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
140dijit.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
162dijit.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
184dijit.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
198dijit.range.create = function(/*Window?*/ win){ // TODO: for 2.0, replace optional window param w/mandatory window or document param
199 win = win || window;
200 if(win.getSelection){
201 return win.document.createRange();
202 }else{//IE
203 return new dijit.range.W3CRange();
204 }
205};
206
207dijit.range.getSelection = function(/*Window*/ window, /*Boolean?*/ ignoreUpdate){
208 if(window.getSelection){
209 return window.getSelection();
210 }else{//IE
211 var s = new dijit.range.ie.selection(window);
212 if(!ignoreUpdate){
213 s._getCurrentSelection();
214 }
215 return s;
216 }
217};
218
219// TODO: convert to has() test? But remember IE9 issues with quirks vs. standards in main frame vs. iframe.
220if(!window.getSelection){
221 dijit.range.ie = {
222 cachedSelection: {},
223 selection: function(window){
224 this._ranges = [];
225 this.addRange = function(r, /*boolean*/ internal){
226 this._ranges.push(r);
227 if(!internal){
228 r._select();
229 }
230 this.rangeCount = this._ranges.length;
231 };
232 this.removeAllRanges = function(){
233 //don't detach, the range may be used later
234// for(var i=0;i<this._ranges.length;i++){
235// this._ranges[i].detach();
236// }
237 this._ranges = [];
238 this.rangeCount = 0;
239 };
240 var _initCurrentRange = function(){
241 var r = window.document.selection.createRange();
242 var type = window.document.selection.type.toUpperCase();
243 if(type == "CONTROL"){
244 //TODO: multiple range selection(?)
245 return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
246 }else{
247 return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
248 }
249 };
250 this.getRangeAt = function(i){
251 return this._ranges[i];
252 };
253 this._getCurrentSelection = function(){
254 this.removeAllRanges();
255 var r = _initCurrentRange();
256 if(r){
257 this.addRange(r, true);
258 this.isCollapsed = r.collapsed;
259 }else{
260 this.isCollapsed = true;
261 }
262 };
263 },
264 decomposeControlRange: function(range){
265 var firstnode = range.item(0), lastnode = range.item(range.length - 1);
266 var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
267 var startOffset = dijit.range.getIndex(firstnode, startContainer).o[0];
268 var endOffset = dijit.range.getIndex(lastnode, endContainer).o[0] + 1;
269 return [startContainer, startOffset,endContainer, endOffset];
270 },
271 getEndPoint: function(range, end){
272 var atmrange = range.duplicate();
273 atmrange.collapse(!end);
274 var cmpstr = 'EndTo' + (end ? 'End' : 'Start');
275 var parentNode = atmrange.parentElement();
276
277 var startnode, startOffset, lastNode;
278 if(parentNode.childNodes.length > 0){
279 array.every(parentNode.childNodes, function(node, i){
280 var calOffset;
281 if(node.nodeType != 3){
282 atmrange.moveToElementText(node);
283
284 if(atmrange.compareEndPoints(cmpstr, range) > 0){
285 //startnode = node.previousSibling;
286 if(lastNode && lastNode.nodeType == 3){
287 //where shall we put the start? in the text node or after?
288 startnode = lastNode;
289 calOffset = true;
290 }else{
291 startnode = parentNode;
292 startOffset = i;
293 return false;
294 }
295 }else{
296 if(i == parentNode.childNodes.length - 1){
297 startnode = parentNode;
298 startOffset = parentNode.childNodes.length;
299 return false;
300 }
301 }
302 }else{
303 if(i == parentNode.childNodes.length - 1){//at the end of this node
304 startnode = node;
305 calOffset = true;
306 }
307 }
308 // try{
309 if(calOffset && startnode){
310 var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
311 if(prevnode){
312 startnode = prevnode.nextSibling;
313 }else{
314 startnode = parentNode.firstChild; //firstChild must be a text node
315 }
316 var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
317 prevnode = prevnodeobj[0];
318 var lenoffset = prevnodeobj[1];
319 if(prevnode){
320 atmrange.moveToElementText(prevnode);
321 atmrange.collapse(false);
322 }else{
323 atmrange.moveToElementText(parentNode);
324 }
325 atmrange.setEndPoint(cmpstr, range);
326 startOffset = atmrange.text.length - lenoffset;
327
328 return false;
329 }
330 // }catch(e){ debugger }
331 lastNode = node;
332 return true;
333 });
334 }else{
335 startnode = parentNode;
336 startOffset = 0;
337 }
338
339 //if at the end of startnode and we are dealing with start container, then
340 //move the startnode to nextSibling if it is a text node
341 //TODO: do this for end container?
342 if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){
343 var nextnode = startnode.nextSibling;
344 if(nextnode && nextnode.nodeType == 3){
345 startnode = nextnode;
346 startOffset = 0;
347 }
348 }
349 return [startnode, startOffset];
350 },
351 setEndPoint: function(range, container, offset){
352 //text node
353 var atmrange = range.duplicate(), node, len;
354 if(container.nodeType != 3){ //normal node
355 if(offset > 0){
356 node = container.childNodes[offset - 1];
357 if(node){
358 if(node.nodeType == 3){
359 container = node;
360 offset = node.length;
361 //pass through
362 }else{
363 if(node.nextSibling && node.nextSibling.nodeType == 3){
364 container = node.nextSibling;
365 offset = 0;
366 //pass through
367 }else{
368 atmrange.moveToElementText(node.nextSibling ? node : container);
369 var parent = node.parentNode;
370 var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling);
371 atmrange.collapse(false);
372 parent.removeChild(tempNode);
373 }
374 }
375 }
376 }else{
377 atmrange.moveToElementText(container);
378 atmrange.collapse(true);
379 }
380 }
381 if(container.nodeType == 3){
382 var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
383 var prevnode = prevnodeobj[0];
384 len = prevnodeobj[1];
385 if(prevnode){
386 atmrange.moveToElementText(prevnode);
387 atmrange.collapse(false);
388 //if contentEditable is not inherit, the above collapse won't make the end point
389 //in the correctly position: it always has a -1 offset, so compensate it
390 if(prevnode.contentEditable != 'inherit'){
391 len++;
392 }
393 }else{
394 atmrange.moveToElementText(container.parentNode);
395 atmrange.collapse(true);
396
397 // Correct internal cursor position
398 // http://bugs.dojotoolkit.org/ticket/15578
399 atmrange.move('character', 1);
400 atmrange.move('character', -1);
401 }
402
403 offset += len;
404 if(offset > 0){
405 if(atmrange.move('character', offset) != offset){
406 console.error('Error when moving!');
407 }
408 }
409 }
410
411 return atmrange;
412 },
413 decomposeTextRange: function(range){
414 var tmpary = dijit.range.ie.getEndPoint(range);
415 var startContainer = tmpary[0], startOffset = tmpary[1];
416 var endContainer = tmpary[0], endOffset = tmpary[1];
417
418 if(range.htmlText.length){
419 if(range.htmlText == range.text){ //in the same text node
420 endOffset = startOffset + range.text.length;
421 }else{
422 tmpary = dijit.range.ie.getEndPoint(range, true);
423 endContainer = tmpary[0],endOffset = tmpary[1];
424// if(startContainer.tagName == "BODY"){
425// startContainer = startContainer.firstChild;
426// }
427 }
428 }
429 return [startContainer, startOffset, endContainer, endOffset];
430 },
431 setRange: function(range, startContainer, startOffset, endContainer, endOffset, collapsed){
432 var start = dijit.range.ie.setEndPoint(range, startContainer, startOffset);
433
434 range.setEndPoint('StartToStart', start);
435 if(!collapsed){
436 var end = dijit.range.ie.setEndPoint(range, endContainer, endOffset);
437 }
438 range.setEndPoint('EndToEnd', end || start);
439
440 return range;
441 }
442 };
443
444declare("dijit.range.W3CRange",null, {
445 constructor: function(){
446 if(arguments.length>0){
447 this.setStart(arguments[0][0],arguments[0][1]);
448 this.setEnd(arguments[0][2],arguments[0][3]);
449 }else{
450 this.commonAncestorContainer = null;
451 this.startContainer = null;
452 this.startOffset = 0;
453 this.endContainer = null;
454 this.endOffset = 0;
455 this.collapsed = true;
456 }
457 },
458 _updateInternal: function(){
459 if(this.startContainer !== this.endContainer){
460 this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
461 }else{
462 this.commonAncestorContainer = this.startContainer;
463 }
464 this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
465 },
466 setStart: function(node, offset){
467 offset=parseInt(offset);
468 if(this.startContainer === node && this.startOffset == offset){
469 return;
470 }
471 delete this._cachedBookmark;
472
473 this.startContainer = node;
474 this.startOffset = offset;
475 if(!this.endContainer){
476 this.setEnd(node, offset);
477 }else{
478 this._updateInternal();
479 }
480 },
481 setEnd: function(node, offset){
482 offset=parseInt(offset);
483 if(this.endContainer === node && this.endOffset == offset){
484 return;
485 }
486 delete this._cachedBookmark;
487
488 this.endContainer = node;
489 this.endOffset = offset;
490 if(!this.startContainer){
491 this.setStart(node, offset);
492 }else{
493 this._updateInternal();
494 }
495 },
496 setStartAfter: function(node, offset){
497 this._setPoint('setStart', node, offset, 1);
498 },
499 setStartBefore: function(node, offset){
500 this._setPoint('setStart', node, offset, 0);
501 },
502 setEndAfter: function(node, offset){
503 this._setPoint('setEnd', node, offset, 1);
504 },
505 setEndBefore: function(node, offset){
506 this._setPoint('setEnd', node, offset, 0);
507 },
508 _setPoint: function(what, node, offset, ext){
509 var index = dijit.range.getIndex(node, node.parentNode).o;
510 this[what](node.parentNode, index.pop()+ext);
511 },
512 _getIERange: function(){
513 var r = (this._body || this.endContainer.ownerDocument.body).createTextRange();
514 dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed);
515 return r;
516 },
517 getBookmark: function(){
518 this._getIERange();
519 return this._cachedBookmark;
520 },
521 _select: function(){
522 var r = this._getIERange();
523 r.select();
524 },
525 deleteContents: function(){
526 var s = this.startContainer, r = this._getIERange();
527 if(s.nodeType === 3 && !this.startOffset){
528 //if the range starts at the beginning of a
529 //text node, move it to before the textnode
530 //to make sure the range is still valid
531 //after deleteContents() finishes
532 this.setStartBefore(s);
533 }
534 r.pasteHTML('');
535 this.endContainer = this.startContainer;
536 this.endOffset = this.startOffset;
537 this.collapsed = true;
538 },
539 cloneRange: function(){
540 var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
541 this.endContainer,this.endOffset]);
542 r._body = this._body;
543 return r;
544 },
545 detach: function(){
546 this._body = null;
547 this.commonAncestorContainer = null;
548 this.startContainer = null;
549 this.startOffset = 0;
550 this.endContainer = null;
551 this.endOffset = 0;
552 this.collapsed = true;
553}
554});
555} //if(!window.getSelection)
556
557
558return dijit.range;
559});