]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/NodeList-manipulate.js
2 Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if(!dojo
._hasResource
["dojo.NodeList-manipulate"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo
._hasResource
["dojo.NodeList-manipulate"] = true;
10 dojo
.provide("dojo.NodeList-manipulate");
13 dojo["NodeList-manipulate"] = {
14 // summary: Adds a chainable methods to dojo.query() / Nodelist instances for manipulating HTML
15 // and DOM nodes and their properties.
19 //TODO: add a way to parse for widgets in the injected markup?
22 function getText(/*DOMNode*/node
){
24 // recursion method for text() to use. Gets text value for a node.
26 // Juse uses nodedValue so things like <br/> tags do not end up in
27 // the text as any sort of line return.
28 var text
= "", ch
= node
.childNodes
;
29 for(var i
= 0, n
; n
= ch
[i
]; i
++){
42 function getWrapInsertion(/*DOMNode*/node
){
44 // finds the innermost element to use for wrap insertion.
46 //Make it easy, assume single nesting, no siblings.
47 while(node
.childNodes
[0] && node
.childNodes
[0].nodeType
== 1){
48 node
= node
.childNodes
[0];
50 return node
; //DOMNode
53 function makeWrapNode(/*DOMNode||String*/html
, /*DOMNode*/refNode
){
55 // convert HTML into nodes if it is not already a node.
56 if(typeof html
== "string"){
57 html
= dojo
._toDom(html
, (refNode
&& refNode
.ownerDocument
));
58 if(html
.nodeType
== 11){
59 //DocumentFragment cannot handle cloneNode, so choose first child.
60 html
= html
.childNodes
[0];
62 }else if(html
.nodeType
== 1 && html
.parentNode
){
63 //This element is already in the DOM clone it, but not its children.
64 html
= html
.cloneNode(false);
66 return html
; /*DOMNode*/
69 dojo
.extend(dojo
.NodeList
, {
70 _placeMultiple: function(/*String||Node||NodeList*/query
, /*String*/position
){
72 // private method for inserting queried nodes into all nodes in this NodeList
73 // at different positions. Differs from NodeList.place because it will clone
74 // the nodes in this NodeList if the query matches more than one element.
75 var nl2
= typeof query
== "string" || query
.nodeType
? dojo
.query(query
) : query
;
77 for(var i
= 0; i
< nl2
.length
; i
++){
78 //Go backwards in DOM to make dom insertions easier via insertBefore
80 var length
= this.length
;
81 for(var j
= length
- 1, item
; item
= this[j
]; j
--){
83 //Need to clone the item. This also means
84 //it needs to be added to the current NodeList
85 //so it can also be the target of other chaining operations.
86 item
= this._cloneNode(item
);
90 dojo
.place(item
, refNode
, position
);
92 refNode
.parentNode
.insertBefore(item
, refNode
);
99 //Add the toAdd items to the current NodeList. Build up list of args
102 toAdd
.unshift(this.length
- 1);
103 Array
.prototype.splice
.apply(this, toAdd
);
106 return this; //dojo.NodeList
109 innerHTML: function(/*String?||DOMNode?|NodeList?*/value
){
111 // allows setting the innerHTML of each node in the NodeList,
112 // if there is a value passed in, otherwise, reads the innerHTML value of the first node.
114 // This method is simpler than the dojo.NodeList.html() method provided by
115 // `dojo.NodeList-html`. This method just does proper innerHTML insertion of HTML fragments,
116 // and it allows for the innerHTML to be read for the first node in the node list.
117 // Since dojo.NodeList-html already took the "html" name, this method is called
118 // "innerHTML". However, if dojo.NodeList-html has not been loaded yet, this
119 // module will define an "html" method that can be used instead. Be careful if you
120 // are working in an environment where it is possible that dojo.NodeList-html could
121 // have been loaded, since its definition of "html" will take precedence.
122 // The nodes represented by the value argument will be cloned if more than one
123 // node is in this NodeList. The nodes in this NodeList are returned in the "set"
124 // usage of this method, not the HTML that was inserted.
126 // if no value is passed, the result is String, the innerHTML of the first node.
127 // If a value is passed, the return is this dojo.NodeList
129 // assume a DOM created by this markup:
130 // | <div id="foo"></div>
131 // | <div id="bar"></div>
132 // This code inserts <p>Hello World</p> into both divs:
133 // | dojo.query("div").innerHTML("<p>Hello World</p>");
135 // assume a DOM created by this markup:
136 // | <div id="foo"><p>Hello Mars</p></div>
137 // | <div id="bar"><p>Hello World</p></div>
138 // This code returns "<p>Hello Mars</p>":
139 // | var message = dojo.query("div").innerHTML();
140 if(arguments
.length
){
141 return this.addContent(value
, "only"); //dojo.NodeList
143 return this[0].innerHTML
; //String
148 html: function(value){
150 // see the information for "innerHTML". "html" is an alias for "innerHTML", but is
151 // only defined if dojo.NodeList-html has not been loaded.
153 // An alias for the "innerHTML" method, but only defined if there is not an existing
154 // "html" method on dojo.NodeList. Be careful if you are working in an environment
155 // where it is possible that dojo.NodeList-html could have been loaded, since its
156 // definition of "html" will take precedence. If you are not sure if dojo.NodeList-html
157 // could be loaded, use the "innerHTML" method.
158 // value: String?||DOMNode?||NodeList?
159 // optional. The HTML fragment to use as innerHTML. If value is not passed, then the innerHTML
160 // of the first element in this NodeList is returned.
162 // if no value is passed, the result is String, the innerHTML of the first node.
163 // If a value is passed, the return is this dojo.NodeList
164 return; // dojo.NodeList
169 text: function(/*String*/value
){
171 // allows setting the text value of each node in the NodeList,
172 // if there is a value passed in, otherwise, returns the text value for all the
173 // nodes in the NodeList in one string.
175 // assume a DOM created by this markup:
176 // | <div id="foo"></div>
177 // | <div id="bar"></div>
178 // This code inserts "Hello World" into both divs:
179 // | dojo.query("div").text("Hello World");
181 // assume a DOM created by this markup:
182 // | <div id="foo"><p>Hello Mars <span>today</span></p></div>
183 // | <div id="bar"><p>Hello World</p></div>
184 // This code returns "Hello Mars today":
185 // | var message = dojo.query("div").text();
187 // if no value is passed, the result is String, the text value of the first node.
188 // If a value is passed, the return is this dojo.NodeList
189 if(arguments
.length
){
190 for(var i
= 0, node
; node
= this[i
]; i
++){
191 if(node
.nodeType
== 1){
193 node
.appendChild(node
.ownerDocument
.createTextNode(value
));
196 return this; //dojo.NodeList
199 for(i
= 0; node
= this[i
]; i
++){
200 result
+= getText(node
);
202 return result
; //String
206 val: function(/*String||Array*/value
){
208 // If a value is passed, allows seting the value property of form elements in this
209 // NodeList, or properly selecting/checking the right value for radio/checkbox/select
210 // elements. If no value is passed, the value of the first node in this NodeList
213 // if no value is passed, the result is String or an Array, for the value of the
215 // If a value is passed, the return is this dojo.NodeList
217 // assume a DOM created by this markup:
218 // | <input type="text" value="foo">
219 // | <select multiple>
220 // | <option value="red" selected>Red</option>
221 // | <option value="blue">Blue</option>
222 // | <option value="yellow" selected>Yellow</option>
224 // This code gets and sets the values for the form fields above:
225 // | dojo.query('[type="text"]').val(); //gets value foo
226 // | dojo.query('[type="text"]').val("bar"); //sets the input's value to "bar"
227 // | dojo.query("select").val() //gets array value ["red", "yellow"]
228 // | dojo.query("select").val(["blue", "yellow"]) //Sets the blue and yellow options to selected.
230 //Special work for input elements.
231 if(arguments
.length
){
232 var isArray
= dojo
.isArray(value
);
233 for(var index
= 0, node
; node
= this[index
]; index
++){
234 var name
= node
.nodeName
.toUpperCase();
235 var type
= node
.type
;
236 var newValue
= isArray
? value
[index
] : value
;
238 if(name
== "SELECT"){
239 var opts
= node
.options
;
240 for(var i
= 0; i
< opts
.length
; i
++){
243 opt
.selected
= (dojo
.indexOf(value
, opt
.value
) != -1);
245 opt
.selected
= (opt
.value
== newValue
);
248 }else if(type
== "checkbox" || type
== "radio"){
249 node
.checked
= (node
.value
== newValue
);
251 node
.value
= newValue
;
254 return this; //dojo.NodeList
256 //node already declared above.
258 if(!node
|| node
.nodeType
!= 1){
261 value
= node
.value
|| "";
262 if(node
.nodeName
.toUpperCase() == "SELECT" && node
.multiple
){
263 //A multivalued selectbox. Do the pain.
265 //opts declared above in if block.
267 //i declared above in if block;
268 for(i
= 0; i
< opts
.length
; i
++){
269 //opt declared above in if block
272 value
.push(opt
.value
);
279 return value
; //String||Array
283 append: function(/*String||DOMNode||NodeList*/content
){
285 // appends the content to every node in the NodeList.
287 // The content will be cloned if the length of NodeList
288 // is greater than 1. Only the DOM nodes are cloned, not
289 // any attached event handlers.
291 // dojo.NodeList, the nodes currently in this NodeList will be returned,
292 // not the appended content.
294 // assume a DOM created by this markup:
295 // | <div id="foo"><p>Hello Mars</p></div>
296 // | <div id="bar"><p>Hello World</p></div>
297 // Running this code:
298 // | dojo.query("div").append("<span>append</span>");
299 // Results in this DOM structure:
300 // | <div id="foo"><p>Hello Mars</p><span>append</span></div>
301 // | <div id="bar"><p>Hello World</p><span>append</span></div>
302 return this.addContent(content
, "last"); //dojo.NodeList
305 appendTo: function(/*String*/query
){
307 // appends nodes in this NodeList to the nodes matched by
308 // the query passed to appendTo.
310 // The nodes in this NodeList will be cloned if the query
311 // matches more than one element. Only the DOM nodes are cloned, not
312 // any attached event handlers.
314 // dojo.NodeList, the nodes currently in this NodeList will be returned,
315 // not the matched nodes from the query.
317 // assume a DOM created by this markup:
318 // | <span>append</span>
319 // | <p>Hello Mars</p>
320 // | <p>Hello World</p>
321 // Running this code:
322 // | dojo.query("span").appendTo("p");
323 // Results in this DOM structure:
324 // | <p>Hello Mars<span>append</span></p>
325 // | <p>Hello World<span>append</span></p>
326 return this._placeMultiple(query
, "last"); //dojo.NodeList
329 prepend: function(/*String||DOMNode||NodeList*/content
){
331 // prepends the content to every node in the NodeList.
333 // The content will be cloned if the length of NodeList
334 // is greater than 1. Only the DOM nodes are cloned, not
335 // any attached event handlers.
337 // dojo.NodeList, the nodes currently in this NodeList will be returned,
338 // not the appended content.
339 // assume a DOM created by this markup:
340 // | <div id="foo"><p>Hello Mars</p></div>
341 // | <div id="bar"><p>Hello World</p></div>
342 // Running this code:
343 // | dojo.query("div").prepend("<span>prepend</span>");
344 // Results in this DOM structure:
345 // | <div id="foo"><span>prepend</span><p>Hello Mars</p></div>
346 // | <div id="bar"><span>prepend</span><p>Hello World</p></div>
347 return this.addContent(content
, "first"); //dojo.NodeList
350 prependTo: function(/*String*/query
){
352 // prepends nodes in this NodeList to the nodes matched by
353 // the query passed to prependTo.
355 // The nodes in this NodeList will be cloned if the query
356 // matches more than one element. Only the DOM nodes are cloned, not
357 // any attached event handlers.
359 // dojo.NodeList, the nodes currently in this NodeList will be returned,
360 // not the matched nodes from the query.
362 // assume a DOM created by this markup:
363 // | <span>prepend</span>
364 // | <p>Hello Mars</p>
365 // | <p>Hello World</p>
366 // Running this code:
367 // | dojo.query("span").prependTo("p");
368 // Results in this DOM structure:
369 // | <p><span>prepend</span>Hello Mars</p>
370 // | <p><span>prepend</span>Hello World</p>
371 return this._placeMultiple(query
, "first"); //dojo.NodeList
374 after: function(/*String||Element||NodeList*/content
){
376 // Places the content after every node in the NodeList.
378 // The content will be cloned if the length of NodeList
379 // is greater than 1. Only the DOM nodes are cloned, not
380 // any attached event handlers.
382 // dojo.NodeList, the nodes currently in this NodeList will be returned,
383 // not the appended content.
385 // assume a DOM created by this markup:
386 // | <div id="foo"><p>Hello Mars</p></div>
387 // | <div id="bar"><p>Hello World</p></div>
388 // Running this code:
389 // | dojo.query("div").after("<span>after</span>");
390 // Results in this DOM structure:
391 // | <div id="foo"><p>Hello Mars</p></div><span>after</span>
392 // | <div id="bar"><p>Hello World</p></div><span>after</span>
393 return this.addContent(content
, "after"); //dojo.NodeList
396 insertAfter: function(/*String*/query
){
398 // The nodes in this NodeList will be placed after the nodes
399 // matched by the query passed to insertAfter.
401 // The nodes in this NodeList will be cloned if the query
402 // matches more than one element. Only the DOM nodes are cloned, not
403 // any attached event handlers.
405 // dojo.NodeList, the nodes currently in this NodeList will be returned,
406 // not the matched nodes from the query.
408 // assume a DOM created by this markup:
409 // | <span>after</span>
410 // | <p>Hello Mars</p>
411 // | <p>Hello World</p>
412 // Running this code:
413 // | dojo.query("span").insertAfter("p");
414 // Results in this DOM structure:
415 // | <p>Hello Mars</p><span>after</span>
416 // | <p>Hello World</p><span>after</span>
417 return this._placeMultiple(query
, "after"); //dojo.NodeList
420 before: function(/*String||DOMNode||NodeList*/content
){
422 // Places the content before every node in the NodeList.
424 // The content will be cloned if the length of NodeList
425 // is greater than 1. Only the DOM nodes are cloned, not
426 // any attached event handlers.
428 // dojo.NodeList, the nodes currently in this NodeList will be returned,
429 // not the appended content.
431 // assume a DOM created by this markup:
432 // | <div id="foo"><p>Hello Mars</p></div>
433 // | <div id="bar"><p>Hello World</p></div>
434 // Running this code:
435 // | dojo.query("div").before("<span>before</span>");
436 // Results in this DOM structure:
437 // | <span>before</span><div id="foo"><p>Hello Mars</p></div>
438 // | <span>before</span><div id="bar"><p>Hello World</p></div>
439 return this.addContent(content
, "before"); //dojo.NodeList
442 insertBefore: function(/*String*/query
){
444 // The nodes in this NodeList will be placed after the nodes
445 // matched by the query passed to insertAfter.
447 // The nodes in this NodeList will be cloned if the query
448 // matches more than one element. Only the DOM nodes are cloned, not
449 // any attached event handlers.
451 // dojo.NodeList, the nodes currently in this NodeList will be returned,
452 // not the matched nodes from the query.
454 // assume a DOM created by this markup:
455 // | <span>before</span>
456 // | <p>Hello Mars</p>
457 // | <p>Hello World</p>
458 // Running this code:
459 // | dojo.query("span").insertBefore("p");
460 // Results in this DOM structure:
461 // | <span>before</span><p>Hello Mars</p>
462 // | <span>before</span><p>Hello World</p>
463 return this._placeMultiple(query
, "before"); //dojo.NodeList
467 remove: function(simpleFilter){
469 // alias for dojo.NodeList's orphan method. Removes elements
470 // in this list that match the simple filter from their parents
471 // and returns them as a new NodeList.
472 // simpleFilter: String
473 // single-expression CSS rule. For example, ".thinger" or
474 // "#someId[attrName='value']" but not "div > span". In short,
475 // anything which does not invoke a descent to evaluate but
476 // can instead be used to test a single node is acceptable.
479 return; // dojo.NodeList
482 remove
: dojo
.NodeList
.prototype.orphan
,
484 wrap: function(/*String||DOMNode*/html
){
486 // Wrap each node in the NodeList with html passed to wrap.
488 // html will be cloned if the NodeList has more than one
489 // element. Only DOM nodes are cloned, not any attached
492 // dojo.NodeList, the nodes in the current NodeList will be returned,
493 // not the nodes from html argument.
495 // assume a DOM created by this markup:
498 // Running this code:
499 // | dojo.query("b").wrap("<div><span></span></div>");
500 // Results in this DOM structure:
501 // | <div><span><b>one</b></span></div>
502 // | <div><span><b>two</b></span></div>
504 html
= makeWrapNode(html
, this[0]);
506 //Now cycle through the elements and do the insertion.
507 for(var i
= 0, node
; node
= this[i
]; i
++){
508 //Always clone because if html is used to hold one of
509 //the "this" nodes, then on the clone of html it will contain
510 //that "this" node, and that would be bad.
511 var clone
= this._cloneNode(html
);
513 node
.parentNode
.replaceChild(clone
, node
);
515 //Find deepest element and insert old node in it.
516 var insertion
= getWrapInsertion(clone
);
517 insertion
.appendChild(node
);
520 return this; //dojo.NodeList
523 wrapAll: function(/*String||DOMNode*/html
){
525 // Insert html where the first node in this NodeList lives, then place all
526 // nodes in this NodeList as the child of the html.
528 // dojo.NodeList, the nodes in the current NodeList will be returned,
529 // not the nodes from html argument.
531 // assume a DOM created by this markup:
532 // | <div class="container">
533 // | <div class="red">Red One</div>
534 // | <div class="blue">Blue One</div>
535 // | <div class="red">Red Two</div>
536 // | <div class="blue">Blue Two</div>
538 // Running this code:
539 // | dojo.query(".red").wrapAll('<div class="allRed"></div>');
540 // Results in this DOM structure:
541 // | <div class="container">
542 // | <div class="allRed">
543 // | <div class="red">Red One</div>
544 // | <div class="red">Red Two</div>
546 // | <div class="blue">Blue One</div>
547 // | <div class="blue">Blue Two</div>
550 html
= makeWrapNode(html
, this[0]);
552 //Place the wrap HTML in place of the first node.
553 this[0].parentNode
.replaceChild(html
, this[0]);
555 //Now cycle through the elements and move them inside
557 var insertion
= getWrapInsertion(html
);
558 for(var i
= 0, node
; node
= this[i
]; i
++){
559 insertion
.appendChild(node
);
562 return this; //dojo.NodeList
565 wrapInner: function(/*String||DOMNode*/html
){
567 // For each node in the NodeList, wrap all its children with the passed in html.
569 // html will be cloned if the NodeList has more than one
570 // element. Only DOM nodes are cloned, not any attached
573 // dojo.NodeList, the nodes in the current NodeList will be returned,
574 // not the nodes from html argument.
576 // assume a DOM created by this markup:
577 // | <div class="container">
578 // | <div class="red">Red One</div>
579 // | <div class="blue">Blue One</div>
580 // | <div class="red">Red Two</div>
581 // | <div class="blue">Blue Two</div>
583 // Running this code:
584 // | dojo.query(".red").wrapInner('<span class="special"></span>');
585 // Results in this DOM structure:
586 // | <div class="container">
587 // | <div class="red"><span class="special">Red One</span></div>
588 // | <div class="blue">Blue One</div>
589 // | <div class="red"><span class="special">Red Two</span></div>
590 // | <div class="blue">Blue Two</div>
593 html
= makeWrapNode(html
, this[0]);
594 for(var i
= 0; i
< this.length
; i
++){
595 //Always clone because if html is used to hold one of
596 //the "this" nodes, then on the clone of html it will contain
597 //that "this" node, and that would be bad.
598 var clone
= this._cloneNode(html
);
600 //Need to convert the childNodes to an array since wrapAll modifies the
601 //DOM and can change the live childNodes NodeList.
602 this._wrap(dojo
._toArray(this[i
].childNodes
), null, this._NodeListCtor
).wrapAll(clone
);
605 return this; //dojo.NodeList
608 replaceWith: function(/*String||DOMNode||NodeList*/content
){
610 // Replaces each node in ths NodeList with the content passed to replaceWith.
612 // The content will be cloned if the length of NodeList
613 // is greater than 1. Only the DOM nodes are cloned, not
614 // any attached event handlers.
616 // The nodes currently in this NodeList will be returned, not the replacing content.
617 // Note that the returned nodes have been removed from the DOM.
619 // assume a DOM created by this markup:
620 // | <div class="container">
621 // | <div class="red">Red One</div>
622 // | <div class="blue">Blue One</div>
623 // | <div class="red">Red Two</div>
624 // | <div class="blue">Blue Two</div>
626 // Running this code:
627 // | dojo.query(".red").replaceWith('<div class="green">Green</div>');
628 // Results in this DOM structure:
629 // | <div class="container">
630 // | <div class="green">Green</div>
631 // | <div class="blue">Blue One</div>
632 // | <div class="green">Green</div>
633 // | <div class="blue">Blue Two</div>
635 content
= this._normalize(content
, this[0]);
636 for(var i
= 0, node
; node
= this[i
]; i
++){
637 this._place(content
, node
, "before", i
> 0);
638 node
.parentNode
.removeChild(node
);
640 return this; //dojo.NodeList
643 replaceAll: function(/*String*/query
){
645 // replaces nodes matched by the query passed to replaceAll with the nodes
648 // The nodes in this NodeList will be cloned if the query
649 // matches more than one element. Only the DOM nodes are cloned, not
650 // any attached event handlers.
652 // The nodes currently in this NodeList will be returned, not the matched nodes
653 // from the query. The nodes currently in this NodeLIst could have
654 // been cloned, so the returned NodeList will include the cloned nodes.
656 // assume a DOM created by this markup:
657 // | <div class="container">
658 // | <div class="spacer">___</div>
659 // | <div class="red">Red One</div>
660 // | <div class="spacer">___</div>
661 // | <div class="blue">Blue One</div>
662 // | <div class="spacer">___</div>
663 // | <div class="red">Red Two</div>
664 // | <div class="spacer">___</div>
665 // | <div class="blue">Blue Two</div>
667 // Running this code:
668 // | dojo.query(".red").replaceAll(".blue");
669 // Results in this DOM structure:
670 // | <div class="container">
671 // | <div class="spacer">___</div>
672 // | <div class="spacer">___</div>
673 // | <div class="red">Red One</div>
674 // | <div class="red">Red Two</div>
675 // | <div class="spacer">___</div>
676 // | <div class="spacer">___</div>
677 // | <div class="red">Red One</div>
678 // | <div class="red">Red Two</div>
680 var nl
= dojo
.query(query
);
681 var content
= this._normalize(this, this[0]);
682 for(var i
= 0, node
; node
= nl
[i
]; i
++){
683 this._place(content
, node
, "before", i
> 0);
684 node
.parentNode
.removeChild(node
);
686 return this; //dojo.NodeList
691 // Clones all the nodes in this NodeList and returns them as a new NodeList.
693 // Only the DOM nodes are cloned, not any attached event handlers.
695 // dojo.NodeList, a cloned set of the original nodes.
697 // assume a DOM created by this markup:
698 // | <div class="container">
699 // | <div class="red">Red One</div>
700 // | <div class="blue">Blue One</div>
701 // | <div class="red">Red Two</div>
702 // | <div class="blue">Blue Two</div>
704 // Running this code:
705 // | dojo.query(".red").clone().appendTo(".container");
706 // Results in this DOM structure:
707 // | <div class="container">
708 // | <div class="red">Red One</div>
709 // | <div class="blue">Blue One</div>
710 // | <div class="red">Red Two</div>
711 // | <div class="blue">Blue Two</div>
712 // | <div class="red">Red One</div>
713 // | <div class="red">Red Two</div>
716 //TODO: need option to clone events?
718 for(var i
= 0; i
< this.length
; i
++){
719 ary
.push(this._cloneNode(this[i
]));
721 return this._wrap(ary
, this, this._NodeListCtor
); //dojo.NodeList
725 //set up html method if one does not exist
726 if(!dojo
.NodeList
.prototype.html
){
727 dojo
.NodeList
.prototype.html
= dojo
.NodeList
.prototype.innerHTML
;