source: Dev/branches/rest-dojo-ui/client/dojo/NodeList-dom.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 16.2 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        /*===== var NodeList = dojo.NodeList; =====*/
3        var magicGuard = function(a){
4                // summary:
5                //              the guard function for dojo.attr() and dojo.style()
6                return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
7        };
8
9        var orphan = function(node){
10                // summary:
11                //              function to orphan nodes
12                var p = node.parentNode;
13                if(p){
14                        p.removeChild(node);
15                }
16        };
17        // FIXME: should we move orphan() to dojo.html?
18
19        var NodeList = query.NodeList,
20                awc = NodeList._adaptWithCondition,
21                aafe = NodeList._adaptAsForEach,
22                aam = NodeList._adaptAsMap;
23
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);
28                        }
29                        // setter
30                        return module.set(node, name, value);
31                };
32        }
33
34        lang.extend(NodeList, {
35                _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
36                        // summary:
37                        //              normalizes data to an array of items to insert.
38                        // description:
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).
47
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.
53
54                        var parse = content.parse === true;
55
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;
60                        }
61
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);
68                                }else{
69                                        content = [content];
70                                }
71                        }else if(!lang.isArrayLike(content)){
72                                content = [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);
77                        }
78
79                        //Pass around the parse info
80                        if(parse){
81                                content._runParse = true;
82                        }
83                        return content; //Array
84                },
85
86                _cloneNode: function(/*DOMNode*/ node){
87                        // summary:
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);
92                },
93
94                _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
95                        // summary:
96                        //              private utility to handle placing an array of nodes relative to another node.
97                        // description:
98                        //              Allows for cloning the nodes in the array, and for
99                        //              optionally parsing widgets, if ary._runParse is true.
100
101                        //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
102                        if(refNode.nodeType != 1 && position == "only"){
103                                return;
104                        }
105                        var rNode = refNode, tempNode;
106
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]);
112
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){
117                                        if(!tempNode){
118                                                tempNode = rNode.ownerDocument.createElement("div");
119                                        }
120                                        tempNode.appendChild(node);
121                                        dojo.parser.parse(tempNode);
122                                        node = tempNode.firstChild;
123                                        while(tempNode.firstChild){
124                                                tempNode.removeChild(tempNode.firstChild);
125                                        }
126                                }
127
128                                if(i == length - 1){
129                                        domCtr.place(node, rNode, position);
130                                }else{
131                                        rNode.parentNode.insertBefore(node, rNode);
132                                }
133                                rNode = node;
134                        }
135                },
136
137                /*=====
138                position: function(){
139                        // summary:
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.
143
144                        return dojo.map(this, dojo.position); // Array
145                },
146
147                attr: function(property, value){
148                        // summary:
149                        //              gets or sets the DOM attribute for every element in the
150                        //              NodeList. See also `dojo.attr`
151                        // property: String
152                        //              the attribute to get/set
153                        // value: String?
154                        //              optional. The value to set the property to
155                        // returns:
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
158                        // example:
159                        //              Make all nodes with a particular class focusable:
160                        //      |       dojo.query(".focusable").attr("tabIndex", -1);
161                        // example:
162                        //              Disable a group of buttons:
163                        //      |       dojo.query("button.group").attr("disabled", true);
164                        // example:
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
169                        return; // Array
170                },
171
172                style: function(property, value){
173                        // summary:
174                        //              gets or sets the CSS property for every element in the NodeList
175                        // property: String
176                        //              the CSS property to get/set, in JavaScript notation
177                        //              ("lineHieght" instead of "line-height")
178                        // value: String?
179                        //              optional. The value to set the property to
180                        // returns:
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
184                        return; // Array
185                },
186
187                addClass: function(className){
188                        // summary:
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
194                },
195
196                removeClass: function(className){
197                        // summary:
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
202                        //              will be deleted.
203                        // returns:
204                        //              dojo.NodeList, this list
205                        return; // dojo.NodeList
206                },
207
208                toggleClass: function(className, condition){
209                        // summary:
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.
214                        // className: String
215                        //              the CSS class to add
216                        return; // dojo.NodeList
217                },
218
219                empty: function(){
220                        // summary:
221                        //              clears all content from each node in the list. Effectively
222                        //              equivalent to removing all child nodes from every item in
223                        //              the list.
224                        return this.forEach("item.innerHTML='';"); // dojo.NodeList
225                        // FIXME: should we be checking for and/or disposing of widgets below these nodes?
226                },
227                =====*/
228
229                // useful html methods
230                attr: awc(getSet(domAttr), magicGuard),
231                style: awc(getSet(domStyle), magicGuard),
232
233                addClass: aafe(domCls.add),
234                removeClass: aafe(domCls.remove),
235                replaceClass: aafe(domCls.replace),
236                toggleClass: aafe(domCls.toggle),
237
238                empty: aafe(domCtr.empty),
239                removeAttr: aafe(domAttr.remove),
240
241                position: aam(domGeom.position),
242                marginBox: aam(domGeom.getMarginBox),
243
244                // FIXME: connectPublisher()? connectRunOnce()?
245
246                /*
247                destroy: function(){
248                        // summary:
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?
252                },
253                */
254
255                place: function(/*String||Node*/ queryOrNode, /*String*/ position){
256                        // summary:
257                        //              places elements of this node list relative to the first element matched
258                        //              by queryOrNode. Returns the original NodeList. See: `dojo.place`
259                        // queryOrNode:
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.
263                        // position:
264                        //              can be one of:
265                        //              |       "last" (default)
266                        //              |       "first"
267                        //              |       "before"
268                        //              |       "after"
269                        //              |       "only"
270                        //              |       "replace"
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
274                },
275
276                orphan: function(/*String?*/ filter){
277                        // summary:
278                        //              removes elements in this list that match the filter
279                        //              from their parents and returns them as a new NodeList.
280                        // filter:
281                        //              CSS selector like ".foo" or "div > span"
282                        // returns:
283                        //              `dojo.NodeList` containing the orphaned elements
284                        return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo.NodeList
285                },
286
287                adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
288                        // summary:
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.
296                        // position:
297                        //              can be one of:
298                        //              |       "last" (default)
299                        //              |       "first"
300                        //              |       "before"
301                        //              |       "after"
302                        //              |       "only"
303                        //              |       "replace"
304                        //              or an offset in the childNodes property
305                        return query(queryOrListOrNode).place(this[0], position)._stash(this);  // dojo.NodeList
306                },
307
308                // FIXME: do we need this?
309                query: function(/*String*/ queryStr){
310                        // summary:
311                        //              Returns a new list whose members match the passed query,
312                        //              assuming elements of the current NodeList as the root for
313                        //              each search.
314                        // example:
315                        //              assume a DOM created by this markup:
316                        //      |       <div id="foo">
317                        //      |               <p>
318                        //      |                       bacon is tasty, <span>dontcha think?</span>
319                        //      |               </p>
320                        //      |       </div>
321                        //      |       <div id="bar">
322                        //      |               <p>great comedians may not be funny <span>in person</span></p>
323                        //      |       </div>
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");
329
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){
337                                                ret.push(subNode);
338                                        }
339                                });
340                        });
341                        return ret._stash(this);        // dojo.NodeList
342                },
343
344                filter: function(/*String|Function*/ filter){
345                        // summary:
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
349                        //              objects.
350                        // filter:
351                        //              If a string, a CSS rule like ".thinger" or "div > span".
352                        // example:
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");
358                        // example:
359                        //              the same filtering using a CSS selector
360                        //              |       dojo.query("*").filter("p").styles("backgroundColor", "yellow");
361
362                        var a = arguments, items = this, start = 0;
363                        if(typeof filter == "string"){ // inline'd type check
364                                items = query._filterResult(this, a[0]);
365                                if(a.length == 1){
366                                        // if we only got a string query, pass back the filtered results
367                                        return items._stash(this); // dojo.NodeList
368                                }
369                                // if we got a callback, run it over the filtered items
370                                start = 1;
371                        }
372                        return this._wrap(array.filter(items, a[start], a[start + 1]), this);   // dojo.NodeList
373                },
374
375                /*
376                // FIXME: should this be "copyTo" and include parenting info?
377                clone: function(){
378                        // summary:
379                        //              creates node clones of each element of this list
380                        //              and returns a new list containing the clones
381                },
382                */
383
384                addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){
385                        // summary:
386                        //              add a node, NodeList or some HTML as a string to every item in the
387                        //              list.  Returns the original list.
388                        // description:
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
392                        //              each item.
393                        // content:
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".
404                        // position:
405                        //              can be one of:
406                        //              |       "last"||"end" (default)
407                        //              |       "first||"start"
408                        //              |       "before"
409                        //              |       "after"
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
413                        // example:
414                        //              appends content to the end if the position is omitted
415                        //      |       dojo.query("h3 > p").addContent("hey there!");
416                        // example:
417                        //              add something to the front of each element that has a
418                        //              "thinger" property:
419                        //      |       dojo.query("[thinger]").addContent("...", "first");
420                        // example:
421                        //              adds a header before each element of the list
422                        //      |       dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
423                        // example:
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"));
427                        // example:
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>',
432                        //                      id: "user332",
433                        //                      name: "Mr. Anderson"
434                        //              });
435                        // example:
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>',
441                        //                      parse: true,
442                        //                      text: "Send"
443                        //              });
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);
447                        }
448                        return this; //dojo.NodeList
449                }
450        });
451
452        /*===== return dojo.NodeList; =====*/
453        return NodeList;
454});
Note: See TracBrowser for help on using the repository browser.