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