source: Dev/trunk/src/client/dojo/dom-construct.js

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

Added Dojo 1.9.3 release.

File size: 12.3 KB
Line 
1define(["exports", "./_base/kernel", "./sniff", "./_base/window", "./dom", "./dom-attr"],
2                function(exports, dojo, has, win, dom, attr){
3        // module:
4        //              dojo/dom-construct
5        // summary:
6        //              This module defines the core dojo DOM construction API.
7
8        // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42
9
10        // support stuff for toDom()
11        var tagWrap = {
12                        option: ["select"],
13                        tbody: ["table"],
14                        thead: ["table"],
15                        tfoot: ["table"],
16                        tr: ["table", "tbody"],
17                        td: ["table", "tbody", "tr"],
18                        th: ["table", "thead", "tr"],
19                        legend: ["fieldset"],
20                        caption: ["table"],
21                        colgroup: ["table"],
22                        col: ["table", "colgroup"],
23                        li: ["ul"]
24                },
25                reTag = /<\s*([\w\:]+)/,
26                masterNode = {}, masterNum = 0,
27                masterName = "__" + dojo._scopeName + "ToDomId";
28
29        // generate start/end tag strings to use
30        // for the injection for each special tag wrap case.
31        for(var param in tagWrap){
32                if(tagWrap.hasOwnProperty(param)){
33                        var tw = tagWrap[param];
34                        tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">";
35                        tw.post = "</" + tw.reverse().join("></") + ">";
36                        // the last line is destructive: it reverses the array,
37                        // but we don't care at this point
38                }
39        }
40
41        var html5domfix;
42        if(has("ie") <= 8){
43                html5domfix = function(doc){
44                        doc.__dojo_html5_tested = "yes";
45                        var div = create('div', {innerHTML: "<nav>a</nav>", style: {visibility: "hidden"}}, doc.body);
46                        if(div.childNodes.length !== 1){
47                                ('abbr article aside audio canvas details figcaption figure footer header ' +
48                                'hgroup mark meter nav output progress section summary time video').replace(
49                                        /\b\w+\b/g, function(n){
50                                                doc.createElement(n);
51                                        }
52                                );
53                        }
54                        destroy(div);
55                }
56        }
57
58        function _insertBefore(/*DomNode*/ node, /*DomNode*/ ref){
59                var parent = ref.parentNode;
60                if(parent){
61                        parent.insertBefore(node, ref);
62                }
63        }
64
65        function _insertAfter(/*DomNode*/ node, /*DomNode*/ ref){
66                // summary:
67                //              Try to insert node after ref
68                var parent = ref.parentNode;
69                if(parent){
70                        if(parent.lastChild == ref){
71                                parent.appendChild(node);
72                        }else{
73                                parent.insertBefore(node, ref.nextSibling);
74                        }
75                }
76        }
77
78        exports.toDom = function toDom(frag, doc){
79                // summary:
80                //              instantiates an HTML fragment returning the corresponding DOM.
81                // frag: String
82                //              the HTML fragment
83                // doc: DocumentNode?
84                //              optional document to use when creating DOM nodes, defaults to
85                //              dojo/_base/window.doc if not specified.
86                // returns:
87                //              Document fragment, unless it's a single node in which case it returns the node itself
88                // example:
89                //              Create a table row:
90                //      |       require(["dojo/dom-construct"], function(domConstruct){
91                //      |               var tr = domConstruct.toDom("<tr><td>First!</td></tr>");
92                //      |       });
93
94                doc = doc || win.doc;
95                var masterId = doc[masterName];
96                if(!masterId){
97                        doc[masterName] = masterId = ++masterNum + "";
98                        masterNode[masterId] = doc.createElement("div");
99                }
100
101                if(has("ie") <= 8){
102                        if(!doc.__dojo_html5_tested && doc.body){
103                                html5domfix(doc);
104                        }
105                }
106
107                // make sure the frag is a string.
108                frag += "";
109
110                // find the starting tag, and get node wrapper
111                var match = frag.match(reTag),
112                        tag = match ? match[1].toLowerCase() : "",
113                        master = masterNode[masterId],
114                        wrap, i, fc, df;
115                if(match && tagWrap[tag]){
116                        wrap = tagWrap[tag];
117                        master.innerHTML = wrap.pre + frag + wrap.post;
118                        for(i = wrap.length; i; --i){
119                                master = master.firstChild;
120                        }
121                }else{
122                        master.innerHTML = frag;
123                }
124
125                // one node shortcut => return the node itself
126                if(master.childNodes.length == 1){
127                        return master.removeChild(master.firstChild); // DOMNode
128                }
129
130                // return multiple nodes as a document fragment
131                df = doc.createDocumentFragment();
132                while((fc = master.firstChild)){ // intentional assignment
133                        df.appendChild(fc);
134                }
135                return df; // DocumentFragment
136        };
137
138        exports.place = function place(/*DOMNode|String*/ node, /*DOMNode|String*/ refNode, /*String|Number?*/ position){
139                // summary:
140                //              Attempt to insert node into the DOM, choosing from various positioning options.
141                //              Returns the first argument resolved to a DOM node.
142                // node: DOMNode|String
143                //              id or node reference, or HTML fragment starting with "<" to place relative to refNode
144                // refNode: DOMNode|String
145                //              id or node reference to use as basis for placement
146                // position: String|Number?
147                //              string noting the position of node relative to refNode or a
148                //              number indicating the location in the childNodes collection of refNode.
149                //              Accepted string values are:
150                //
151                //              - before
152                //              - after
153                //              - replace
154                //              - only
155                //              - first
156                //              - last
157                //
158                //              "first" and "last" indicate positions as children of refNode, "replace" replaces refNode,
159                //              "only" replaces all children.  position defaults to "last" if not specified
160                // returns: DOMNode
161                //              Returned values is the first argument resolved to a DOM node.
162                //
163                //              .place() is also a method of `dojo/NodeList`, allowing `dojo/query` node lookups.
164                // example:
165                //              Place a node by string id as the last child of another node by string id:
166                //      |       require(["dojo/dom-construct"], function(domConstruct){
167                //      |               domConstruct.place("someNode", "anotherNode");
168                //      |       });
169                // example:
170                //              Place a node by string id before another node by string id
171                //      |       require(["dojo/dom-construct"], function(domConstruct){
172                //      |               domConstruct.place("someNode", "anotherNode", "before");
173                //      |       });
174                // example:
175                //              Create a Node, and place it in the body element (last child):
176                //      |       require(["dojo/dom-construct", "dojo/_base/window"
177                //      |       ], function(domConstruct, win){
178                //      |               domConstruct.place("<div></div>", win.body());
179                //      |       });
180                // example:
181                //              Put a new LI as the first child of a list by id:
182                //      |       require(["dojo/dom-construct"], function(domConstruct){
183                //      |               domConstruct.place("<li></li>", "someUl", "first");
184                //      |       });
185
186                refNode = dom.byId(refNode);
187                if(typeof node == "string"){ // inline'd type check
188                        node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node);
189                }
190                if(typeof position == "number"){ // inline'd type check
191                        var cn = refNode.childNodes;
192                        if(!cn.length || cn.length <= position){
193                                refNode.appendChild(node);
194                        }else{
195                                _insertBefore(node, cn[position < 0 ? 0 : position]);
196                        }
197                }else{
198                        switch(position){
199                                case "before":
200                                        _insertBefore(node, refNode);
201                                        break;
202                                case "after":
203                                        _insertAfter(node, refNode);
204                                        break;
205                                case "replace":
206                                        refNode.parentNode.replaceChild(node, refNode);
207                                        break;
208                                case "only":
209                                        exports.empty(refNode);
210                                        refNode.appendChild(node);
211                                        break;
212                                case "first":
213                                        if(refNode.firstChild){
214                                                _insertBefore(node, refNode.firstChild);
215                                                break;
216                                        }
217                                        // else fallthrough...
218                                default: // aka: last
219                                        refNode.appendChild(node);
220                        }
221                }
222                return node; // DomNode
223        };
224
225        var create = exports.create = function create(/*DOMNode|String*/ tag, /*Object*/ attrs, /*DOMNode|String?*/ refNode, /*String?*/ pos){
226                // summary:
227                //              Create an element, allowing for optional attribute decoration
228                //              and placement.
229                // description:
230                //              A DOM Element creation function. A shorthand method for creating a node or
231                //              a fragment, and allowing for a convenient optional attribute setting step,
232                //              as well as an optional DOM placement reference.
233                //
234                //              Attributes are set by passing the optional object through `dojo.setAttr`.
235                //              See `dojo.setAttr` for noted caveats and nuances, and API if applicable.
236                //
237                //              Placement is done via `dojo.place`, assuming the new node to be the action
238                //              node, passing along the optional reference node and position.
239                // tag: DOMNode|String
240                //              A string of the element to create (eg: "div", "a", "p", "li", "script", "br"),
241                //              or an existing DOM node to process.
242                // attrs: Object
243                //              An object-hash of attributes to set on the newly created node.
244                //              Can be null, if you don't want to set any attributes/styles.
245                //              See: `dojo.setAttr` for a description of available attributes.
246                // refNode: DOMNode|String?
247                //              Optional reference node. Used by `dojo.place` to place the newly created
248                //              node somewhere in the dom relative to refNode. Can be a DomNode reference
249                //              or String ID of a node.
250                // pos: String?
251                //              Optional positional reference. Defaults to "last" by way of `dojo.place`,
252                //              though can be set to "first","after","before","last", "replace" or "only"
253                //              to further control the placement of the new node relative to the refNode.
254                //              'refNode' is required if a 'pos' is specified.
255                // example:
256                //              Create a DIV:
257                //      |       require(["dojo/dom-construct"], function(domConstruct){
258                //      |               var n = domConstruct.create("div");
259                //      |       });
260                //
261                // example:
262                //              Create a DIV with content:
263                //      |       require(["dojo/dom-construct"], function(domConstruct){
264                //      |               var n = domConstruct.create("div", { innerHTML:"<p>hi</p>" });
265                //      |       });
266                //
267                // example:
268                //              Place a new DIV in the BODY, with no attributes set
269                //      |       require(["dojo/dom-construct"], function(domConstruct){
270                //      |               var n = domConstruct.create("div", null, dojo.body());
271                //      |       });
272                //
273                // example:
274                //              Create an UL, and populate it with LI's. Place the list as the first-child of a
275                //              node with id="someId":
276                //      |       require(["dojo/dom-construct", "dojo/_base/array"],
277                //      |       function(domConstruct, arrayUtil){
278                //      |               var ul = domConstruct.create("ul", null, "someId", "first");
279                //      |               var items = ["one", "two", "three", "four"];
280                //      |               arrayUtil.forEach(items, function(data){
281                //      |                       domConstruct.create("li", { innerHTML: data }, ul);
282                //      |               });
283                //      |       });
284                //
285                // example:
286                //              Create an anchor, with an href. Place in BODY:
287                //      |       require(["dojo/dom-construct"], function(domConstruct){
288                //      |               domConstruct.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body());
289                //      |       });
290
291                var doc = win.doc;
292                if(refNode){
293                        refNode = dom.byId(refNode);
294                        doc = refNode.ownerDocument;
295                }
296                if(typeof tag == "string"){ // inline'd type check
297                        tag = doc.createElement(tag);
298                }
299                if(attrs){ attr.set(tag, attrs); }
300                if(refNode){ exports.place(tag, refNode, pos); }
301                return tag; // DomNode
302        };
303
304        function _empty(/*DomNode*/ node){
305                if(node.canHaveChildren){
306                        try{
307                                // fast path
308                                node.innerHTML = "";
309                                return;
310                        }catch(e){
311                                // innerHTML is readOnly (e.g. TABLE (sub)elements in quirks mode)
312                                // Fall through (saves bytes)
313                        }
314                }
315                // SVG/strict elements don't support innerHTML/canHaveChildren, and OBJECT/APPLET elements in quirks node have canHaveChildren=false
316                for(var c; c = node.lastChild;){ // intentional assignment
317                        _destroy(c, node); // destroy is better than removeChild so TABLE subelements are removed in proper order
318                }
319        }
320
321        exports.empty = function empty(/*DOMNode|String*/ node){
322                // summary:
323                //              safely removes all children of the node.
324                // node: DOMNode|String
325                //              a reference to a DOM node or an id.
326                // example:
327                //              Destroy node's children byId:
328                //      |       require(["dojo/dom-construct"], function(domConstruct){
329                //      |               domConstruct.empty("someId");
330                //      |       });
331
332                _empty(dom.byId(node));
333        };
334
335
336        function _destroy(/*DomNode*/ node, /*DomNode*/ parent){
337                // in IE quirks, node.canHaveChildren can be false but firstChild can be non-null (OBJECT/APPLET)
338                if(node.firstChild){
339                        _empty(node);
340                }
341                if(parent){
342                        // removeNode(false) doesn't leak in IE 6+, but removeChild() and removeNode(true) are known to leak under IE 8- while 9+ is TBD.
343                        // In IE quirks mode, PARAM nodes as children of OBJECT/APPLET nodes have a removeNode method that does nothing and
344                        // the parent node has canHaveChildren=false even though removeChild correctly removes the PARAM children.
345                        // In IE, SVG/strict nodes don't have a removeNode method nor a canHaveChildren boolean.
346                        has("ie") && parent.canHaveChildren && "removeNode" in node ? node.removeNode(false) : parent.removeChild(node);
347                }
348        }
349        var destroy = exports.destroy = function destroy(/*DOMNode|String*/ node){
350                // summary:
351                //              Removes a node from its parent, clobbering it and all of its
352                //              children.
353                //
354                // description:
355                //              Removes a node from its parent, clobbering it and all of its
356                //              children. Function only works with DomNodes, and returns nothing.
357                //
358                // node: DOMNode|String
359                //              A String ID or DomNode reference of the element to be destroyed
360                //
361                // example:
362                //              Destroy a node byId:
363                //      |       require(["dojo/dom-construct"], function(domConstruct){
364                //      |               domConstruct.destroy("someId");
365                //      |       });
366
367                node = dom.byId(node);
368                if(!node){ return; }
369                _destroy(node, node.parentNode);
370        };
371});
Note: See TracBrowser for help on using the repository browser.