source: Dev/trunk/src/client/dojo/NodeList-dom.js @ 501

Last change on this file since 501 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 18.5 KB
Line 
1define(["./_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
3        // module:
4        //              dojo/NodeList-dom.js
5
6        /*=====
7         return function(){
8                 // summary:
9                 //             Adds DOM related methods to NodeList, and returns NodeList constructor.
10         };
11         =====*/
12
13        var magicGuard = function(a){
14                // summary:
15                //              the guard function for dojo/dom-attr() and dojo/dom-style()
16                return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
17        };
18
19        var orphan = function(node){
20                // summary:
21                //              function to orphan nodes
22                var p = node.parentNode;
23                if(p){
24                        p.removeChild(node);
25                }
26        };
27        // FIXME: should we move orphan() to dojo/_base/html?
28
29        var NodeList = query.NodeList,
30                awc = NodeList._adaptWithCondition,
31                aafe = NodeList._adaptAsForEach,
32                aam = NodeList._adaptAsMap;
33
34        function getSet(module){
35                return function(node, name, value){
36                        if(arguments.length == 2){
37                                return module[typeof name == "string" ? "get" : "set"](node, name);
38                        }
39                        // setter
40                        return module.set(node, name, value);
41                };
42        }
43
44        lang.extend(NodeList, {
45                _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
46                        // summary:
47                        //              normalizes data to an array of items to insert.
48                        // description:
49                        //              If content is an object, it can have special properties "template" and
50                        //              "parse". If "template" is defined, then the template value is run through
51                        //              dojo/string.substitute (if dojo/string.substitute() has been required elsewhere),
52                        //              or if templateFunc is a function on the content, that function will be used to
53                        //              transform the template into a final string to be used for for passing to dojo/dom-construct.toDom().
54                        //              If content.parse is true, then it is remembered for later, for when the content
55                        //              nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets
56                        //              (if dojo/parser has been required elsewhere).
57
58                        //Wanted to just use a DocumentFragment, but for the array/NodeList
59                        //case that meant using cloneNode, but we may not want that.
60                        //Cloning should only happen if the node operations span
61                        //multiple refNodes. Also, need a real array, not a NodeList from the
62                        //DOM since the node movements could change those NodeLists.
63
64                        var parse = content.parse === true;
65
66                        //Do we have an object that needs to be run through a template?
67                        if(typeof content.template == "string"){
68                                var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute);
69                                content = templateFunc ? templateFunc(content.template, content) : content;
70                        }
71
72                        var type = (typeof content);
73                        if(type == "string" || type == "number"){
74                                content = domCtr.toDom(content, (refNode && refNode.ownerDocument));
75                                if(content.nodeType == 11){
76                                        //DocumentFragment. It cannot handle cloneNode calls, so pull out the children.
77                                        content = lang._toArray(content.childNodes);
78                                }else{
79                                        content = [content];
80                                }
81                        }else if(!lang.isArrayLike(content)){
82                                content = [content];
83                        }else if(!lang.isArray(content)){
84                                //To get to this point, content is array-like, but
85                                //not an array, which likely means a DOM NodeList. Convert it now.
86                                content = lang._toArray(content);
87                        }
88
89                        //Pass around the parse info
90                        if(parse){
91                                content._runParse = true;
92                        }
93                        return content; //Array
94                },
95
96                _cloneNode: function(/*DOMNode*/ node){
97                        // summary:
98                        //              private utility to clone a node. Not very interesting in the vanilla
99                        //              dojo/NodeList case, but delegates could do interesting things like
100                        //              clone event handlers if that is derivable from the node.
101                        return node.cloneNode(true);
102                },
103
104                _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
105                        // summary:
106                        //              private utility to handle placing an array of nodes relative to another node.
107                        // description:
108                        //              Allows for cloning the nodes in the array, and for
109                        //              optionally parsing widgets, if ary._runParse is true.
110
111                        //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
112                        if(refNode.nodeType != 1 && position == "only"){
113                                return;
114                        }
115                        var rNode = refNode, tempNode;
116
117                        //Always cycle backwards in case the array is really a
118                        //DOM NodeList and the DOM operations take it out of the live collection.
119                        var length = ary.length;
120                        for(var i = length - 1; i >= 0; i--){
121                                var node = (useClone ? this._cloneNode(ary[i]) : ary[i]);
122
123                                //If need widget parsing, use a temp node, instead of waiting after inserting into
124                                //real DOM because we need to start widget parsing at one node up from current node,
125                                //which could cause some already parsed widgets to be parsed again.
126                                if(ary._runParse && dojo.parser && dojo.parser.parse){
127                                        if(!tempNode){
128                                                tempNode = rNode.ownerDocument.createElement("div");
129                                        }
130                                        tempNode.appendChild(node);
131                                        dojo.parser.parse(tempNode);
132                                        node = tempNode.firstChild;
133                                        while(tempNode.firstChild){
134                                                tempNode.removeChild(tempNode.firstChild);
135                                        }
136                                }
137
138                                if(i == length - 1){
139                                        domCtr.place(node, rNode, position);
140                                }else{
141                                        rNode.parentNode.insertBefore(node, rNode);
142                                }
143                                rNode = node;
144                        }
145                },
146
147
148                position: aam(domGeom.position),
149                /*=====
150                position: function(){
151                        // summary:
152                        //              Returns border-box objects (x/y/w/h) of all elements in a node list
153                        //              as an Array (*not* a NodeList). Acts like `dojo/dom-geometry-position`, though
154                        //              assumes the node passed is each node in this list.
155
156                        return dojo.map(this, dojo.position); // Array
157                },
158                =====*/
159
160                attr: awc(getSet(domAttr), magicGuard),
161                /*=====
162                attr: function(property, value){
163                        // summary:
164                        //              gets or sets the DOM attribute for every element in the
165                        //              NodeList. See also `dojo/dom-attr`
166                        // property: String
167                        //              the attribute to get/set
168                        // value: String?
169                        //              optional. The value to set the property to
170                        // returns:
171                        //              if no value is passed, the result is an array of attribute values
172                        //              If a value is passed, the return is this NodeList
173                        // example:
174                        //              Make all nodes with a particular class focusable:
175                        //      |       require(["dojo/query", "dojo/NodeList-dom"], function(query){
176                        //      |               query(".focusable").attr("tabIndex", -1);
177                        //      |       });
178                        // example:
179                        //              Disable a group of buttons:
180                        //      |       require(["dojo/query", "dojo/NodeList-dom"], function(query){
181                        //      |               query("button.group").attr("disabled", true);
182                        //      |       });
183                        // example:
184                        //              innerHTML can be assigned or retrieved as well:
185                        //      |       // get the innerHTML (as an array) for each list item
186                        //      |       require(["dojo/query", "dojo/NodeList-dom"], function(query){
187                        //      |               var ih = query("li.replaceable").attr("innerHTML");
188                        //      |       });
189                        return; // dojo/NodeList|Array
190                },
191                =====*/
192
193                style: awc(getSet(domStyle), magicGuard),
194                /*=====
195                style: function(property, value){
196                        // summary:
197                        //              gets or sets the CSS property for every element in the NodeList
198                        // property: String
199                        //              the CSS property to get/set, in JavaScript notation
200                        //              ("lineHieght" instead of "line-height")
201                        // value: String?
202                        //              optional. The value to set the property to
203                        // returns:
204                        //              if no value is passed, the result is an array of strings.
205                        //              If a value is passed, the return is this NodeList
206                        return; // dojo/NodeList
207                        return; // Array
208                },
209                =====*/
210
211                addClass: aafe(domCls.add),
212                /*=====
213                addClass: function(className){
214                        // summary:
215                        //              adds the specified class to every node in the list
216                        // className: String|Array
217                        //              A String class name to add, or several space-separated class names,
218                        //              or an array of class names.
219                        return; // dojo/NodeList
220                },
221                =====*/
222
223                removeClass: aafe(domCls.remove),
224                /*=====
225                removeClass: function(className){
226                        // summary:
227                        //              removes the specified class from every node in the list
228                        // className: String|Array?
229                        //              An optional String class name to remove, or several space-separated
230                        //              class names, or an array of class names. If omitted, all class names
231                        //              will be deleted.
232                        // returns:
233                        //              this list
234                        return; // dojo/NodeList
235                },
236                =====*/
237
238                toggleClass: aafe(domCls.toggle),
239                /*=====
240                toggleClass: function(className, condition){
241                        // summary:
242                        //              Adds a class to node if not present, or removes if present.
243                        //              Pass a boolean condition if you want to explicitly add or remove.
244                        // condition: Boolean?
245                        //              If passed, true means to add the class, false means to remove.
246                        // className: String
247                        //              the CSS class to add
248                        return; // dojo/NodeList
249                },
250                =====*/
251
252                replaceClass: aafe(domCls.replace),
253                /*=====
254                replaceClass: function(addClassStr, removeClassStr){
255                        // summary:
256                        //              Replaces one or more classes on a node if not present.
257                        //              Operates more quickly than calling `removeClass()` and `addClass()`
258                        // addClassStr: String|Array
259                        //              A String class name to add, or several space-separated class names,
260                        //              or an array of class names.
261                        // removeClassStr: String|Array?
262                        //              A String class name to remove, or several space-separated class names,
263                        //              or an array of class names.
264                        return; // dojo/NodeList
265                 },
266                 =====*/
267
268                empty: aafe(domCtr.empty),
269                /*=====
270                empty: function(){
271                        // summary:
272                        //              clears all content from each node in the list. Effectively
273                        //              equivalent to removing all child nodes from every item in
274                        //              the list.
275                        return this.forEach("item.innerHTML='';"); // dojo/NodeList
276                        // FIXME: should we be checking for and/or disposing of widgets below these nodes?
277                },
278                =====*/
279
280                removeAttr: aafe(domAttr.remove),
281                /*=====
282                 removeAttr: function(name){
283                        // summary:
284                        //              Removes an attribute from each node in the list.
285                        // name: String
286                        //              the name of the attribute to remove
287                        return;         // dojo/NodeList
288                },
289                =====*/
290
291                marginBox: aam(domGeom.getMarginBox),
292                /*=====
293                marginBox: function(){
294                        // summary:
295                        //              Returns margin-box size of nodes
296                        return; // dojo/NodeList
297                 },
298                 =====*/
299
300                // FIXME: connectPublisher()? connectRunOnce()?
301
302                /*
303                destroy: function(){
304                        // summary:
305                        //              destroys every item in the list.
306                        this.forEach(d.destroy);
307                        // FIXME: should we be checking for and/or disposing of widgets below these nodes?
308                },
309                */
310
311                place: function(/*String||Node*/ queryOrNode, /*String*/ position){
312                        // summary:
313                        //              places elements of this node list relative to the first element matched
314                        //              by queryOrNode. Returns the original NodeList. See: `dojo/dom-construct.place`
315                        // queryOrNode:
316                        //              may be a string representing any valid CSS3 selector or a DOM node.
317                        //              In the selector case, only the first matching element will be used
318                        //              for relative positioning.
319                        // position:
320                        //              can be one of:
321                        //
322                        //              -       "last" (default)
323                        //              -       "first"
324                        //              -       "before"
325                        //              -       "after"
326                        //              -       "only"
327                        //              -       "replace"
328                        //
329                        //              or an offset in the childNodes property
330                        var item = query(queryOrNode)[0];
331                        return this.forEach(function(node){ domCtr.place(node, item, position); }); // dojo/NodeList
332                },
333
334                orphan: function(/*String?*/ filter){
335                        // summary:
336                        //              removes elements in this list that match the filter
337                        //              from their parents and returns them as a new NodeList.
338                        // filter:
339                        //              CSS selector like ".foo" or "div > span"
340                        // returns:
341                        //              NodeList containing the orphaned elements
342                        return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo/NodeList
343                },
344
345                adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
346                        // summary:
347                        //              places any/all elements in queryOrListOrNode at a
348                        //              position relative to the first element in this list.
349                        //              Returns a dojo/NodeList of the adopted elements.
350                        // queryOrListOrNode:
351                        //              a DOM node or a query string or a query result.
352                        //              Represents the nodes to be adopted relative to the
353                        //              first element of this NodeList.
354                        // position:
355                        //              can be one of:
356                        //
357                        //              -       "last" (default)
358                        //              -       "first"
359                        //              -       "before"
360                        //              -       "after"
361                        //              -       "only"
362                        //              -       "replace"
363                        //
364                        //              or an offset in the childNodes property
365                        return query(queryOrListOrNode).place(this[0], position)._stash(this);  // dojo/NodeList
366                },
367
368                // FIXME: do we need this?
369                query: function(/*String*/ queryStr){
370                        // summary:
371                        //              Returns a new list whose members match the passed query,
372                        //              assuming elements of the current NodeList as the root for
373                        //              each search.
374                        // example:
375                        //              assume a DOM created by this markup:
376                        //      |       <div id="foo">
377                        //      |               <p>
378                        //      |                       bacon is tasty, <span>dontcha think?</span>
379                        //      |               </p>
380                        //      |       </div>
381                        //      |       <div id="bar">
382                        //      |               <p>great comedians may not be funny <span>in person</span></p>
383                        //      |       </div>
384                        //              If we are presented with the following definition for a NodeList:
385                        //      |       require(["dojo/dom", "dojo/query", "dojo/NodeList-dom"
386                        //      |       ], function(dom, query){
387                        //      |               var l = new NodeList(dom.byId("foo"), dom.byId("bar"));
388                        //              it's possible to find all span elements under paragraphs
389                        //              contained by these elements with this sub-query:
390                        //      |               var spans = l.query("p span");
391                        //      |       });
392
393                        // FIXME: probably slow
394                        if(!queryStr){ return this; }
395                        var ret = new NodeList;
396                        this.map(function(node){
397                                // FIXME: why would we ever get undefined here?
398                                query(queryStr, node).forEach(function(subNode){
399                                        if(subNode !== undefined){
400                                                ret.push(subNode);
401                                        }
402                                });
403                        });
404                        return ret._stash(this);        // dojo/NodeList
405                },
406
407                filter: function(/*String|Function*/ filter){
408                        // summary:
409                        //              "masks" the built-in javascript filter() method (supported
410                        //              in Dojo via `dojo.filter`) to support passing a simple
411                        //              string filter in addition to supporting filtering function
412                        //              objects.
413                        // filter:
414                        //              If a string, a CSS rule like ".thinger" or "div > span".
415                        // example:
416                        //              "regular" JS filter syntax as exposed in dojo.filter:
417                        //      |       require(["dojo/query", "dojo/NodeList-dom"
418                        //      |       ], function(query){
419                        //      |               query("*").filter(function(item){
420                        //      |                       // highlight every paragraph
421                        //      |                       return (item.nodeName == "p");
422                        //      |               }).style("backgroundColor", "yellow");
423                        //      |       });
424                        // example:
425                        //      the same filtering using a CSS selector
426                        //      |       require(["dojo/query", "dojo/NodeList-dom"
427                        //      |       ], function(query){
428                        //      |               query("*").filter("p").styles("backgroundColor", "yellow");
429                        //      |       });
430                        var a = arguments, items = this, start = 0;
431                        if(typeof filter == "string"){ // inline'd type check
432                                items = query._filterResult(this, a[0]);
433                                if(a.length == 1){
434                                        // if we only got a string query, pass back the filtered results
435                                        return items._stash(this); // dojo/NodeList
436                                }
437                                // if we got a callback, run it over the filtered items
438                                start = 1;
439                        }
440                        return this._wrap(array.filter(items, a[start], a[start + 1]), this);   // dojo/NodeList
441                },
442
443                /*
444                // FIXME: should this be "copyTo" and include parenting info?
445                clone: function(){
446                        // summary:
447                        //              creates node clones of each element of this list
448                        //              and returns a new list containing the clones
449                },
450                */
451
452                addContent: function(/*String||DomNode||Object||dojo/NodeList*/ content, /*String||Integer?*/ position){
453                        // summary:
454                        //              add a node, NodeList or some HTML as a string to every item in the
455                        //              list.  Returns the original list.
456                        // description:
457                        //              a copy of the HTML content is added to each item in the
458                        //              list, with an optional position argument. If no position
459                        //              argument is provided, the content is appended to the end of
460                        //              each item.
461                        // content:
462                        //              DOM node, HTML in string format, a NodeList or an Object. If a DOM node or
463                        //              NodeList, the content will be cloned if the current NodeList has more than one
464                        //              element. Only the DOM nodes are cloned, no event handlers. If it is an Object,
465                        //              it should be an object with at "template" String property that has the HTML string
466                        //              to insert. If dojo.string has already been dojo.required, then dojo.string.substitute
467                        //              will be used on the "template" to generate the final HTML string. Other allowed
468                        //              properties on the object are: "parse" if the HTML
469                        //              string should be parsed for widgets (dojo.require("dojo.parser") to get that
470                        //              option to work), and "templateFunc" if a template function besides dojo.string.substitute
471                        //              should be used to transform the "template".
472                        // position:
473                        //              can be one of:
474                        //
475                        //              -       "last"||"end" (default)
476                        //              -       "first||"start"
477                        //              -       "before"
478                        //              -       "after"
479                        //              -       "replace" (replaces nodes in this NodeList with new content)
480                        //              -       "only" (removes other children of the nodes so new content is the only child)
481                        //
482                        //              or an offset in the childNodes property
483                        // example:
484                        //              appends content to the end if the position is omitted
485                        //      |       require(["dojo/query", "dojo/NodeList-dom"
486                        //      |       ], function(query){
487                        //      |               query("h3 > p").addContent("hey there!");
488                        //      |       });
489                        // example:
490                        //              add something to the front of each element that has a
491                        //              "thinger" property:
492                        //      |       require(["dojo/query", "dojo/NodeList-dom"
493                        //      |       ], function(query){
494                        //      |               query("[thinger]").addContent("...", "first");
495                        //      |       });
496                        // example:
497                        //              adds a header before each element of the list
498                        //      |       require(["dojo/query", "dojo/NodeList-dom"
499                        //      |       ], function(query){
500                        //      |               query(".note").addContent("<h4>NOTE:</h4>", "before");
501                        //      |       });
502                        // example:
503                        //              add a clone of a DOM node to the end of every element in
504                        //              the list, removing it from its existing parent.
505                        //      |       require(["dojo/dom", "dojo/query", "dojo/NodeList-dom"
506                        //      |       ], function(dom, query){
507                        //      |               query(".note").addContent(dom.byId("foo"));
508                        //      |       });
509                        // example:
510                        //              Append nodes from a templatized string.
511                        //      |       require(["dojo/string", "dojo/query", "dojo/NodeList-dom"
512                        //      |       ], function(string, query){
513                        //      |               query(".note").addContent({
514                        //      |                       template: '<b>${id}: </b><span>${name}</span>',
515                        //      |                       id: "user332",
516                        //      |                       name: "Mr. Anderson"
517                        //      |               });
518                        //      |       });
519                        // example:
520                        //              Append nodes from a templatized string that also has widgets parsed.
521                        //      |       require(["dojo/string", "dojo/parser", "dojo/query", "dojo/NodeList-dom"
522                        //      |       ], function(string, parser, query){
523                        //      |               var notes = query(".note").addContent({
524                        //      |                       template: '<button dojoType="dijit/form/Button">${text}</button>',
525                        //      |                       parse: true,
526                        //      |                       text: "Send"
527                        //      |               });
528                        //      |       });
529                        content = this._normalize(content, this[0]);
530                        for(var i = 0, node; (node = this[i]); i++){
531                                if(content.length){
532                                        this._place(content, node, position, i > 0);
533                                }else{
534                                        // if it is an empty array, we empty the target node
535                                        domCtr.empty(node);
536                                }
537                        }
538                        return this; // dojo/NodeList
539                }
540        });
541
542        return NodeList;
543});
Note: See TracBrowser for help on using the repository browser.