[483] | 1 | define(["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 | }); |
---|