]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/dom-construct", ["exports", "./_base/kernel", "./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 | // 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 | function _insertBefore(/*DomNode*/ node, /*DomNode*/ ref){ | |
42 | var parent = ref.parentNode; | |
43 | if(parent){ | |
44 | parent.insertBefore(node, ref); | |
45 | } | |
46 | } | |
47 | ||
48 | function _insertAfter(/*DomNode*/ node, /*DomNode*/ ref){ | |
49 | // summary: | |
50 | // Try to insert node after ref | |
51 | var parent = ref.parentNode; | |
52 | if(parent){ | |
53 | if(parent.lastChild == ref){ | |
54 | parent.appendChild(node); | |
55 | }else{ | |
56 | parent.insertBefore(node, ref.nextSibling); | |
57 | } | |
58 | } | |
59 | } | |
60 | ||
61 | exports.toDom = function toDom(frag, doc){ | |
62 | // summary: | |
63 | // instantiates an HTML fragment returning the corresponding DOM. | |
64 | // frag: String | |
65 | // the HTML fragment | |
66 | // doc: DocumentNode? | |
67 | // optional document to use when creating DOM nodes, defaults to | |
68 | // dojo.doc if not specified. | |
69 | // returns: | |
70 | // Document fragment, unless it's a single node in which case it returns the node itself | |
71 | // example: | |
72 | // Create a table row: | |
73 | // | var tr = dojo.toDom("<tr><td>First!</td></tr>"); | |
74 | ||
75 | doc = doc || win.doc; | |
76 | var masterId = doc[masterName]; | |
77 | if(!masterId){ | |
78 | doc[masterName] = masterId = ++masterNum + ""; | |
79 | masterNode[masterId] = doc.createElement("div"); | |
80 | } | |
81 | ||
82 | // make sure the frag is a string. | |
83 | frag += ""; | |
84 | ||
85 | // find the starting tag, and get node wrapper | |
86 | var match = frag.match(reTag), | |
87 | tag = match ? match[1].toLowerCase() : "", | |
88 | master = masterNode[masterId], | |
89 | wrap, i, fc, df; | |
90 | if(match && tagWrap[tag]){ | |
91 | wrap = tagWrap[tag]; | |
92 | master.innerHTML = wrap.pre + frag + wrap.post; | |
93 | for(i = wrap.length; i; --i){ | |
94 | master = master.firstChild; | |
95 | } | |
96 | }else{ | |
97 | master.innerHTML = frag; | |
98 | } | |
99 | ||
100 | // one node shortcut => return the node itself | |
101 | if(master.childNodes.length == 1){ | |
102 | return master.removeChild(master.firstChild); // DOMNode | |
103 | } | |
104 | ||
105 | // return multiple nodes as a document fragment | |
106 | df = doc.createDocumentFragment(); | |
107 | while((fc = master.firstChild)){ // intentional assignment | |
108 | df.appendChild(fc); | |
109 | } | |
110 | return df; // DocumentFragment | |
111 | }; | |
112 | ||
113 | exports.place = function place(/*DOMNode|String*/ node, /*DOMNode|String*/ refNode, /*String|Number?*/ position){ | |
114 | // summary: | |
115 | // Attempt to insert node into the DOM, choosing from various positioning options. | |
116 | // Returns the first argument resolved to a DOM node. | |
117 | // node: DOMNode|String | |
118 | // id or node reference, or HTML fragment starting with "<" to place relative to refNode | |
119 | // refNode: DOMNode|String | |
120 | // id or node reference to use as basis for placement | |
121 | // position: String|Number? | |
122 | // string noting the position of node relative to refNode or a | |
123 | // number indicating the location in the childNodes collection of refNode. | |
124 | // Accepted string values are: | |
125 | // | |
126 | // - before | |
127 | // - after | |
128 | // - replace | |
129 | // - only | |
130 | // - first | |
131 | // - last | |
132 | // | |
133 | // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, | |
134 | // "only" replaces all children. position defaults to "last" if not specified | |
135 | // returns: DOMNode | |
136 | // Returned values is the first argument resolved to a DOM node. | |
137 | // | |
138 | // .place() is also a method of `dojo/NodeList`, allowing `dojo.query` node lookups. | |
139 | // example: | |
140 | // Place a node by string id as the last child of another node by string id: | |
141 | // | dojo.place("someNode", "anotherNode"); | |
142 | // example: | |
143 | // Place a node by string id before another node by string id | |
144 | // | dojo.place("someNode", "anotherNode", "before"); | |
145 | // example: | |
146 | // Create a Node, and place it in the body element (last child): | |
147 | // | dojo.place("<div></div>", dojo.body()); | |
148 | // example: | |
149 | // Put a new LI as the first child of a list by id: | |
150 | // | dojo.place("<li></li>", "someUl", "first"); | |
151 | ||
152 | refNode = dom.byId(refNode); | |
153 | if(typeof node == "string"){ // inline'd type check | |
154 | node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node); | |
155 | } | |
156 | if(typeof position == "number"){ // inline'd type check | |
157 | var cn = refNode.childNodes; | |
158 | if(!cn.length || cn.length <= position){ | |
159 | refNode.appendChild(node); | |
160 | }else{ | |
161 | _insertBefore(node, cn[position < 0 ? 0 : position]); | |
162 | } | |
163 | }else{ | |
164 | switch(position){ | |
165 | case "before": | |
166 | _insertBefore(node, refNode); | |
167 | break; | |
168 | case "after": | |
169 | _insertAfter(node, refNode); | |
170 | break; | |
171 | case "replace": | |
172 | refNode.parentNode.replaceChild(node, refNode); | |
173 | break; | |
174 | case "only": | |
175 | exports.empty(refNode); | |
176 | refNode.appendChild(node); | |
177 | break; | |
178 | case "first": | |
179 | if(refNode.firstChild){ | |
180 | _insertBefore(node, refNode.firstChild); | |
181 | break; | |
182 | } | |
183 | // else fallthrough... | |
184 | default: // aka: last | |
185 | refNode.appendChild(node); | |
186 | } | |
187 | } | |
188 | return node; // DomNode | |
189 | }; | |
190 | ||
191 | exports.create = function create(/*DOMNode|String*/ tag, /*Object*/ attrs, /*DOMNode|String?*/ refNode, /*String?*/ pos){ | |
192 | // summary: | |
193 | // Create an element, allowing for optional attribute decoration | |
194 | // and placement. | |
195 | // description: | |
196 | // A DOM Element creation function. A shorthand method for creating a node or | |
197 | // a fragment, and allowing for a convenient optional attribute setting step, | |
198 | // as well as an optional DOM placement reference. | |
199 | // | |
200 | // Attributes are set by passing the optional object through `dojo.setAttr`. | |
201 | // See `dojo.setAttr` for noted caveats and nuances, and API if applicable. | |
202 | // | |
203 | // Placement is done via `dojo.place`, assuming the new node to be the action | |
204 | // node, passing along the optional reference node and position. | |
205 | // tag: DOMNode|String | |
206 | // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), | |
207 | // or an existing DOM node to process. | |
208 | // attrs: Object | |
209 | // An object-hash of attributes to set on the newly created node. | |
210 | // Can be null, if you don't want to set any attributes/styles. | |
211 | // See: `dojo.setAttr` for a description of available attributes. | |
212 | // refNode: DOMNode|String? | |
213 | // Optional reference node. Used by `dojo.place` to place the newly created | |
214 | // node somewhere in the dom relative to refNode. Can be a DomNode reference | |
215 | // or String ID of a node. | |
216 | // pos: String? | |
217 | // Optional positional reference. Defaults to "last" by way of `dojo.place`, | |
218 | // though can be set to "first","after","before","last", "replace" or "only" | |
219 | // to further control the placement of the new node relative to the refNode. | |
220 | // 'refNode' is required if a 'pos' is specified. | |
221 | // example: | |
222 | // Create a DIV: | |
223 | // | var n = dojo.create("div"); | |
224 | // | |
225 | // example: | |
226 | // Create a DIV with content: | |
227 | // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" }); | |
228 | // | |
229 | // example: | |
230 | // Place a new DIV in the BODY, with no attributes set | |
231 | // | var n = dojo.create("div", null, dojo.body()); | |
232 | // | |
233 | // example: | |
234 | // Create an UL, and populate it with LI's. Place the list as the first-child of a | |
235 | // node with id="someId": | |
236 | // | var ul = dojo.create("ul", null, "someId", "first"); | |
237 | // | var items = ["one", "two", "three", "four"]; | |
238 | // | dojo.forEach(items, function(data){ | |
239 | // | dojo.create("li", { innerHTML: data }, ul); | |
240 | // | }); | |
241 | // | |
242 | // example: | |
243 | // Create an anchor, with an href. Place in BODY: | |
244 | // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body()); | |
245 | // | |
246 | // example: | |
247 | // Create a `dojo/NodeList()` from a new element (for syntactic sugar): | |
248 | // | dojo.query(dojo.create('div')) | |
249 | // | .addClass("newDiv") | |
250 | // | .onclick(function(e){ console.log('clicked', e.target) }) | |
251 | // | .place("#someNode"); // redundant, but cleaner. | |
252 | ||
253 | var doc = win.doc; | |
254 | if(refNode){ | |
255 | refNode = dom.byId(refNode); | |
256 | doc = refNode.ownerDocument; | |
257 | } | |
258 | if(typeof tag == "string"){ // inline'd type check | |
259 | tag = doc.createElement(tag); | |
260 | } | |
261 | if(attrs){ attr.set(tag, attrs); } | |
262 | if(refNode){ exports.place(tag, refNode, pos); } | |
263 | return tag; // DomNode | |
264 | }; | |
265 | ||
266 | var _empty = has("ie") ? | |
267 | function(/*DomNode*/ node){ | |
268 | try{ | |
269 | node.innerHTML = ""; // really fast when it works | |
270 | }catch(e){ // IE can generate Unknown Error | |
271 | for(var c; c = node.lastChild;){ // intentional assignment | |
272 | _destroy(c, node); // destroy is better than removeChild so TABLE elements are removed in proper order | |
273 | } | |
274 | } | |
275 | } : | |
276 | function(/*DomNode*/ node){ | |
277 | node.innerHTML = ""; | |
278 | }; | |
279 | ||
280 | exports.empty = function empty(/*DOMNode|String*/ node){ | |
281 | // summary: | |
282 | // safely removes all children of the node. | |
283 | // node: DOMNode|String | |
284 | // a reference to a DOM node or an id. | |
285 | // example: | |
286 | // Destroy node's children byId: | |
287 | // | dojo.empty("someId"); | |
288 | // | |
289 | // example: | |
290 | // Destroy all nodes' children in a list by reference: | |
291 | // | dojo.query(".someNode").forEach(dojo.empty); | |
292 | ||
293 | _empty(dom.byId(node)); | |
294 | }; | |
295 | ||
296 | ||
297 | function _destroy(/*DomNode*/ node, /*DomNode*/ parent){ | |
298 | if(node.firstChild){ | |
299 | _empty(node); | |
300 | } | |
301 | if(parent){ | |
302 | parent.removeChild(node); | |
303 | } | |
304 | } | |
305 | exports.destroy = function destroy(/*DOMNode|String*/ node){ | |
306 | // summary: | |
307 | // Removes a node from its parent, clobbering it and all of its | |
308 | // children. | |
309 | // | |
310 | // description: | |
311 | // Removes a node from its parent, clobbering it and all of its | |
312 | // children. Function only works with DomNodes, and returns nothing. | |
313 | // | |
314 | // node: DOMNode|String | |
315 | // A String ID or DomNode reference of the element to be destroyed | |
316 | // | |
317 | // example: | |
318 | // Destroy a node byId: | |
319 | // | dojo.destroy("someId"); | |
320 | // | |
321 | // example: | |
322 | // Destroy all nodes in a list by reference: | |
323 | // | dojo.query(".someNode").forEach(dojo.destroy); | |
324 | ||
325 | node = dom.byId(node); | |
326 | if(!node){ return; } | |
327 | _destroy(node, node.parentNode); | |
328 | }; | |
329 | }); |