1 define("dojo/NodeList-dom", ["./_base/kernel", "./query", "./_base/array", "./_base/lang", "./dom-class", "./dom-construct", "./dom-geometry", "./dom-attr", "./dom-style"], function(dojo, query, array, lang, domCls, domCtr, domGeom, domAttr, domStyle){
2 /*===== var NodeList = dojo.NodeList; =====*/
3 var magicGuard = function(a){
5 // the guard function for dojo.attr() and dojo.style()
6 return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
9 var orphan = function(node){
11 // function to orphan nodes
12 var p = node.parentNode;
17 // FIXME: should we move orphan() to dojo.html?
19 var NodeList = query.NodeList,
20 awc = NodeList._adaptWithCondition,
21 aafe = NodeList._adaptAsForEach,
22 aam = NodeList._adaptAsMap;
24 function getSet(module){
25 return function(node, name, value){
26 if(arguments.length == 2){
27 return module[typeof name == "string" ? "get" : "set"](node, name);
30 return module.set(node, name, value);
34 lang.extend(NodeList, {
35 _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
37 // normalizes data to an array of items to insert.
39 // If content is an object, it can have special properties "template" and
40 // "parse". If "template" is defined, then the template value is run through
41 // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere),
42 // or if templateFunc is a function on the content, that function will be used to
43 // transform the template into a final string to be used for for passing to dojo._toDom.
44 // If content.parse is true, then it is remembered for later, for when the content
45 // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets
46 // (if dojo.parser has been dojo.required elsewhere).
48 //Wanted to just use a DocumentFragment, but for the array/NodeList
49 //case that meant using cloneNode, but we may not want that.
50 //Cloning should only happen if the node operations span
51 //multiple refNodes. Also, need a real array, not a NodeList from the
52 //DOM since the node movements could change those NodeLists.
54 var parse = content.parse === true;
56 //Do we have an object that needs to be run through a template?
57 if(typeof content.template == "string"){
58 var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute);
59 content = templateFunc ? templateFunc(content.template, content) : content;
62 var type = (typeof content);
63 if(type == "string" || type == "number"){
64 content = domCtr.toDom(content, (refNode && refNode.ownerDocument));
65 if(content.nodeType == 11){
66 //DocumentFragment. It cannot handle cloneNode calls, so pull out the children.
67 content = lang._toArray(content.childNodes);
71 }else if(!lang.isArrayLike(content)){
73 }else if(!lang.isArray(content)){
74 //To get to this point, content is array-like, but
75 //not an array, which likely means a DOM NodeList. Convert it now.
76 content = lang._toArray(content);
79 //Pass around the parse info
81 content._runParse = true;
83 return content; //Array
86 _cloneNode: function(/*DOMNode*/ node){
88 // private utility to clone a node. Not very interesting in the vanilla
89 // dojo.NodeList case, but delegates could do interesting things like
90 // clone event handlers if that is derivable from the node.
91 return node.cloneNode(true);
94 _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
96 // private utility to handle placing an array of nodes relative to another node.
98 // Allows for cloning the nodes in the array, and for
99 // optionally parsing widgets, if ary._runParse is true.
101 //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
102 if(refNode.nodeType != 1 && position == "only"){
105 var rNode = refNode, tempNode;
107 //Always cycle backwards in case the array is really a
108 //DOM NodeList and the DOM operations take it out of the live collection.
109 var length = ary.length;
110 for(var i = length - 1; i >= 0; i--){
111 var node = (useClone ? this._cloneNode(ary[i]) : ary[i]);
113 //If need widget parsing, use a temp node, instead of waiting after inserting into
114 //real DOM because we need to start widget parsing at one node up from current node,
115 //which could cause some already parsed widgets to be parsed again.
116 if(ary._runParse && dojo.parser && dojo.parser.parse){
118 tempNode = rNode.ownerDocument.createElement("div");
120 tempNode.appendChild(node);
121 dojo.parser.parse(tempNode);
122 node = tempNode.firstChild;
123 while(tempNode.firstChild){
124 tempNode.removeChild(tempNode.firstChild);
129 domCtr.place(node, rNode, position);
131 rNode.parentNode.insertBefore(node, rNode);
138 position: function(){
140 // Returns border-box objects (x/y/w/h) of all elements in a node list
141 // as an Array (*not* a NodeList). Acts like `dojo.position`, though
142 // assumes the node passed is each node in this list.
144 return dojo.map(this, dojo.position); // Array
147 attr: function(property, value){
149 // gets or sets the DOM attribute for every element in the
150 // NodeList. See also `dojo.attr`
152 // the attribute to get/set
154 // optional. The value to set the property to
156 // if no value is passed, the result is an array of attribute values
157 // If a value is passed, the return is this NodeList
159 // Make all nodes with a particular class focusable:
160 // | dojo.query(".focusable").attr("tabIndex", -1);
162 // Disable a group of buttons:
163 // | dojo.query("button.group").attr("disabled", true);
165 // innerHTML can be assigned or retrieved as well:
166 // | // get the innerHTML (as an array) for each list item
167 // | var ih = dojo.query("li.replaceable").attr("innerHTML");
168 return; // dojo.NodeList
172 style: function(property, value){
174 // gets or sets the CSS property for every element in the NodeList
176 // the CSS property to get/set, in JavaScript notation
177 // ("lineHieght" instead of "line-height")
179 // optional. The value to set the property to
181 // if no value is passed, the result is an array of strings.
182 // If a value is passed, the return is this NodeList
183 return; // dojo.NodeList
187 addClass: function(className){
189 // adds the specified class to every node in the list
190 // className: String|Array
191 // A String class name to add, or several space-separated class names,
192 // or an array of class names.
193 return; // dojo.NodeList
196 removeClass: function(className){
198 // removes the specified class from every node in the list
199 // className: String|Array?
200 // An optional String class name to remove, or several space-separated
201 // class names, or an array of class names. If omitted, all class names
204 // dojo.NodeList, this list
205 return; // dojo.NodeList
208 toggleClass: function(className, condition){
210 // Adds a class to node if not present, or removes if present.
211 // Pass a boolean condition if you want to explicitly add or remove.
212 // condition: Boolean?
213 // If passed, true means to add the class, false means to remove.
215 // the CSS class to add
216 return; // dojo.NodeList
221 // clears all content from each node in the list. Effectively
222 // equivalent to removing all child nodes from every item in
224 return this.forEach("item.innerHTML='';"); // dojo.NodeList
225 // FIXME: should we be checking for and/or disposing of widgets below these nodes?
229 // useful html methods
230 attr: awc(getSet(domAttr), magicGuard),
231 style: awc(getSet(domStyle), magicGuard),
233 addClass: aafe(domCls.add),
234 removeClass: aafe(domCls.remove),
235 replaceClass: aafe(domCls.replace),
236 toggleClass: aafe(domCls.toggle),
238 empty: aafe(domCtr.empty),
239 removeAttr: aafe(domAttr.remove),
241 position: aam(domGeom.position),
242 marginBox: aam(domGeom.getMarginBox),
244 // FIXME: connectPublisher()? connectRunOnce()?
249 // destroys every item in the list.
250 this.forEach(d.destroy);
251 // FIXME: should we be checking for and/or disposing of widgets below these nodes?
255 place: function(/*String||Node*/ queryOrNode, /*String*/ position){
257 // places elements of this node list relative to the first element matched
258 // by queryOrNode. Returns the original NodeList. See: `dojo.place`
260 // may be a string representing any valid CSS3 selector or a DOM node.
261 // In the selector case, only the first matching element will be used
262 // for relative positioning.
265 // | "last" (default)
271 // or an offset in the childNodes property
272 var item = query(queryOrNode)[0];
273 return this.forEach(function(node){ domCtr.place(node, item, position); }); // dojo.NodeList
276 orphan: function(/*String?*/ filter){
278 // removes elements in this list that match the filter
279 // from their parents and returns them as a new NodeList.
281 // CSS selector like ".foo" or "div > span"
283 // `dojo.NodeList` containing the orphaned elements
284 return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo.NodeList
287 adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
289 // places any/all elements in queryOrListOrNode at a
290 // position relative to the first element in this list.
291 // Returns a dojo.NodeList of the adopted elements.
292 // queryOrListOrNode:
293 // a DOM node or a query string or a query result.
294 // Represents the nodes to be adopted relative to the
295 // first element of this NodeList.
298 // | "last" (default)
304 // or an offset in the childNodes property
305 return query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList
308 // FIXME: do we need this?
309 query: function(/*String*/ queryStr){
311 // Returns a new list whose members match the passed query,
312 // assuming elements of the current NodeList as the root for
315 // assume a DOM created by this markup:
318 // | bacon is tasty, <span>dontcha think?</span>
322 // | <p>great comedians may not be funny <span>in person</span></p>
324 // If we are presented with the following definition for a NodeList:
325 // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar"));
326 // it's possible to find all span elements under paragraphs
327 // contained by these elements with this sub-query:
328 // | var spans = l.query("p span");
330 // FIXME: probably slow
331 if(!queryStr){ return this; }
332 var ret = new NodeList;
333 this.map(function(node){
334 // FIXME: why would we ever get undefined here?
335 query(queryStr, node).forEach(function(subNode){
336 if(subNode !== undefined){
341 return ret._stash(this); // dojo.NodeList
344 filter: function(/*String|Function*/ filter){
346 // "masks" the built-in javascript filter() method (supported
347 // in Dojo via `dojo.filter`) to support passing a simple
348 // string filter in addition to supporting filtering function
351 // If a string, a CSS rule like ".thinger" or "div > span".
353 // "regular" JS filter syntax as exposed in dojo.filter:
354 // | dojo.query("*").filter(function(item){
355 // | // highlight every paragraph
356 // | return (item.nodeName == "p");
357 // | }).style("backgroundColor", "yellow");
359 // the same filtering using a CSS selector
360 // | dojo.query("*").filter("p").styles("backgroundColor", "yellow");
362 var a = arguments, items = this, start = 0;
363 if(typeof filter == "string"){ // inline'd type check
364 items = query._filterResult(this, a[0]);
366 // if we only got a string query, pass back the filtered results
367 return items._stash(this); // dojo.NodeList
369 // if we got a callback, run it over the filtered items
372 return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo.NodeList
376 // FIXME: should this be "copyTo" and include parenting info?
379 // creates node clones of each element of this list
380 // and returns a new list containing the clones
384 addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){
386 // add a node, NodeList or some HTML as a string to every item in the
387 // list. Returns the original list.
389 // a copy of the HTML content is added to each item in the
390 // list, with an optional position argument. If no position
391 // argument is provided, the content is appended to the end of
394 // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or
395 // NodeList, the content will be cloned if the current NodeList has more than one
396 // element. Only the DOM nodes are cloned, no event handlers. If it is an Object,
397 // it should be an object with at "template" String property that has the HTML string
398 // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute
399 // will be used on the "template" to generate the final HTML string. Other allowed
400 // properties on the object are: "parse" if the HTML
401 // string should be parsed for widgets (dojo.require("dojo.parser") to get that
402 // option to work), and "templateFunc" if a template function besides dojo.string.substitute
403 // should be used to transform the "template".
406 // | "last"||"end" (default)
410 // | "replace" (replaces nodes in this NodeList with new content)
411 // | "only" (removes other children of the nodes so new content is the only child)
412 // or an offset in the childNodes property
414 // appends content to the end if the position is omitted
415 // | dojo.query("h3 > p").addContent("hey there!");
417 // add something to the front of each element that has a
418 // "thinger" property:
419 // | dojo.query("[thinger]").addContent("...", "first");
421 // adds a header before each element of the list
422 // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
424 // add a clone of a DOM node to the end of every element in
425 // the list, removing it from its existing parent.
426 // | dojo.query(".note").addContent(dojo.byId("foo"));
428 // Append nodes from a templatized string.
429 // dojo.require("dojo.string");
430 // dojo.query(".note").addContent({
431 // template: '<b>${id}: </b><span>${name}</span>',
433 // name: "Mr. Anderson"
436 // Append nodes from a templatized string that also has widgets parsed.
437 // dojo.require("dojo.string");
438 // dojo.require("dojo.parser");
439 // var notes = dojo.query(".note").addContent({
440 // template: '<button dojoType="dijit.form.Button">${text}</button>',
444 content = this._normalize(content, this[0]);
445 for(var i = 0, node; (node = this[i]); i++){
446 this._place(content, node, position, i > 0);
448 return this; //dojo.NodeList
452 /*===== return dojo.NodeList; =====*/