]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | define("dojo/dom-construct", ["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 | on(window, "unload", function(){ | |
253 | _destroyContainer = null; //prevent IE leak | |
254 | }); | |
255 | ||
256 | exports.toDom = function toDom(frag, doc){ | |
257 | doc = doc || win.doc; | |
258 | var masterId = doc[masterName]; | |
259 | if(!masterId){ | |
260 | doc[masterName] = masterId = ++masterNum + ""; | |
261 | masterNode[masterId] = doc.createElement("div"); | |
262 | } | |
263 | ||
264 | // make sure the frag is a string. | |
265 | frag += ""; | |
266 | ||
267 | // find the starting tag, and get node wrapper | |
268 | var match = frag.match(reTag), | |
269 | tag = match ? match[1].toLowerCase() : "", | |
270 | master = masterNode[masterId], | |
271 | wrap, i, fc, df; | |
272 | if(match && tagWrap[tag]){ | |
273 | wrap = tagWrap[tag]; | |
274 | master.innerHTML = wrap.pre + frag + wrap.post; | |
275 | for(i = wrap.length; i; --i){ | |
276 | master = master.firstChild; | |
277 | } | |
278 | }else{ | |
279 | master.innerHTML = frag; | |
280 | } | |
281 | ||
282 | // one node shortcut => return the node itself | |
283 | if(master.childNodes.length == 1){ | |
284 | return master.removeChild(master.firstChild); // DOMNode | |
285 | } | |
286 | ||
287 | // return multiple nodes as a document fragment | |
288 | df = doc.createDocumentFragment(); | |
289 | while(fc = master.firstChild){ // intentional assignment | |
290 | df.appendChild(fc); | |
291 | } | |
292 | return df; // DOMNode | |
293 | }; | |
294 | ||
295 | exports.place = function place(/*DOMNode|String*/node, /*DOMNode|String*/refNode, /*String|Number?*/position){ | |
296 | refNode = dom.byId(refNode); | |
297 | if(typeof node == "string"){ // inline'd type check | |
298 | node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node); | |
299 | } | |
300 | if(typeof position == "number"){ // inline'd type check | |
301 | var cn = refNode.childNodes; | |
302 | if(!cn.length || cn.length <= position){ | |
303 | refNode.appendChild(node); | |
304 | }else{ | |
305 | _insertBefore(node, cn[position < 0 ? 0 : position]); | |
306 | } | |
307 | }else{ | |
308 | switch(position){ | |
309 | case "before": | |
310 | _insertBefore(node, refNode); | |
311 | break; | |
312 | case "after": | |
313 | _insertAfter(node, refNode); | |
314 | break; | |
315 | case "replace": | |
316 | refNode.parentNode.replaceChild(node, refNode); | |
317 | break; | |
318 | case "only": | |
319 | exports.empty(refNode); | |
320 | refNode.appendChild(node); | |
321 | break; | |
322 | case "first": | |
323 | if(refNode.firstChild){ | |
324 | _insertBefore(node, refNode.firstChild); | |
325 | break; | |
326 | } | |
327 | // else fallthrough... | |
328 | default: // aka: last | |
329 | refNode.appendChild(node); | |
330 | } | |
331 | } | |
332 | return node; // DomNode | |
333 | }; | |
334 | ||
335 | exports.create = function create(/*DOMNode|String*/tag, /*Object*/attrs, /*DOMNode?|String?*/refNode, /*String?*/pos){ | |
336 | var doc = win.doc; | |
337 | if(refNode){ | |
338 | refNode = dom.byId(refNode); | |
339 | doc = refNode.ownerDocument; | |
340 | } | |
341 | if(typeof tag == "string"){ // inline'd type check | |
342 | tag = doc.createElement(tag); | |
343 | } | |
344 | if(attrs){ attr.set(tag, attrs); } | |
345 | if(refNode){ exports.place(tag, refNode, pos); } | |
346 | return tag; // DomNode | |
347 | }; | |
348 | ||
349 | exports.empty = | |
350 | has("ie") ? function(node){ | |
351 | node = dom.byId(node); | |
352 | for(var c; c = node.lastChild;){ // intentional assignment | |
353 | exports.destroy(c); | |
354 | } | |
355 | } : | |
356 | function(node){ | |
357 | dom.byId(node).innerHTML = ""; | |
358 | }; | |
359 | ||
360 | exports.destroy = function destroy(/*DOMNode|String*/node){ | |
361 | node = dom.byId(node); | |
362 | try{ | |
363 | var doc = node.ownerDocument; | |
364 | // cannot use _destroyContainer.ownerDocument since this can throw an exception on IE | |
365 | if(!_destroyContainer || _destroyDoc != doc){ | |
366 | _destroyContainer = doc.createElement("div"); | |
367 | _destroyDoc = doc; | |
368 | } | |
369 | _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node); | |
370 | // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature | |
371 | _destroyContainer.innerHTML = ""; | |
372 | }catch(e){ | |
373 | /* squelch */ | |
374 | } | |
375 | }; | |
376 | }); |