Plato on Github
Report Home
dojo/dom-construct.js
Maintainability
59.32
Lines of code
376
Difficulty
51.31
Estimated Errors
1.86
Function weight
By Complexity
By SLOC
define(["exports", "./_base/kernel", "./sniff", "./_base/window", "./dom", "./dom-attr"], function(exports, dojo, has, win, dom, attr){ // module: // dojo/dom-construct // summary: // This module defines the core dojo DOM construction API. // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42 // support stuff for toDom() var tagWrap = { option: ["select"], tbody: ["table"], thead: ["table"], tfoot: ["table"], tr: ["table", "tbody"], td: ["table", "tbody", "tr"], th: ["table", "thead", "tr"], legend: ["fieldset"], caption: ["table"], colgroup: ["table"], col: ["table", "colgroup"], li: ["ul"] }, reTag = /<\s*([\w\:]+)/, masterNode = {}, masterNum = 0, masterName = "__" + dojo._scopeName + "ToDomId"; // generate start/end tag strings to use // for the injection for each special tag wrap case. for(var param in tagWrap){ if(tagWrap.hasOwnProperty(param)){ var tw = tagWrap[param]; tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">"; tw.post = "</" + tw.reverse().join("></") + ">"; // the last line is destructive: it reverses the array, // but we don't care at this point } } var html5domfix; if(has("ie") <= 8){ html5domfix = function(doc){ doc.__dojo_html5_tested = "yes"; var div = create('div', {innerHTML: "<nav>a</nav>", style: {visibility: "hidden"}}, doc.body); if(div.childNodes.length !== 1){ ('abbr article aside audio canvas details figcaption figure footer header ' + 'hgroup mark meter nav output progress section summary time video').replace( /\b\w+\b/g, function(n){ doc.createElement(n); } ); } destroy(div); } } function _insertBefore(/*DomNode*/ node, /*DomNode*/ ref){ var parent = ref.parentNode; if(parent){ parent.insertBefore(node, ref); } } function _insertAfter(/*DomNode*/ node, /*DomNode*/ ref){ // summary: // Try to insert node after ref var parent = ref.parentNode; if(parent){ if(parent.lastChild == ref){ parent.appendChild(node); }else{ parent.insertBefore(node, ref.nextSibling); } } } exports.toDom = function toDom(frag, doc){ // summary: // instantiates an HTML fragment returning the corresponding DOM. // frag: String // the HTML fragment // doc: DocumentNode? // optional document to use when creating DOM nodes, defaults to // dojo/_base/window.doc if not specified. // returns: // Document fragment, unless it's a single node in which case it returns the node itself // example: // Create a table row: // | require(["dojo/dom-construct"], function(domConstruct){ // | var tr = domConstruct.toDom("<tr><td>First!</td></tr>"); // | }); doc = doc || win.doc; var masterId = doc[masterName]; if(!masterId){ doc[masterName] = masterId = ++masterNum + ""; masterNode[masterId] = doc.createElement("div"); } if(has("ie") <= 8){ if(!doc.__dojo_html5_tested && doc.body){ html5domfix(doc); } } // make sure the frag is a string. frag += ""; // find the starting tag, and get node wrapper var match = frag.match(reTag), tag = match ? match[1].toLowerCase() : "", master = masterNode[masterId], wrap, i, fc, df; if(match && tagWrap[tag]){ wrap = tagWrap[tag]; master.innerHTML = wrap.pre + frag + wrap.post; for(i = wrap.length; i; --i){ master = master.firstChild; } }else{ master.innerHTML = frag; } // one node shortcut => return the node itself if(master.childNodes.length == 1){ return master.removeChild(master.firstChild); // DOMNode } // return multiple nodes as a document fragment df = doc.createDocumentFragment(); while((fc = master.firstChild)){ // intentional assignment df.appendChild(fc); } return df; // DocumentFragment }; exports.place = function place(node, refNode, position){ // summary: // Attempt to insert node into the DOM, choosing from various positioning options. // Returns the first argument resolved to a DOM node. // node: DOMNode|DocumentFragment|String // id or node reference, or HTML fragment starting with "<" to place relative to refNode // refNode: DOMNode|String // id or node reference to use as basis for placement // position: String|Number? // string noting the position of node relative to refNode or a // number indicating the location in the childNodes collection of refNode. // Accepted string values are: // // - before // - after // - replace // - only // - first // - last // // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, // "only" replaces all children. position defaults to "last" if not specified // returns: DOMNode // Returned values is the first argument resolved to a DOM node. // // .place() is also a method of `dojo/NodeList`, allowing `dojo/query` node lookups. // example: // Place a node by string id as the last child of another node by string id: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("someNode", "anotherNode"); // | }); // example: // Place a node by string id before another node by string id // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("someNode", "anotherNode", "before"); // | }); // example: // Create a Node, and place it in the body element (last child): // | require(["dojo/dom-construct", "dojo/_base/window" // | ], function(domConstruct, win){ // | domConstruct.place("<div></div>", win.body()); // | }); // example: // Put a new LI as the first child of a list by id: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("<li></li>", "someUl", "first"); // | }); refNode = dom.byId(refNode); if(typeof node == "string"){ // inline'd type check node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node); } if(typeof position == "number"){ // inline'd type check var cn = refNode.childNodes; if(!cn.length || cn.length <= position){ refNode.appendChild(node); }else{ _insertBefore(node, cn[position < 0 ? 0 : position]); } }else{ switch(position){ case "before": _insertBefore(node, refNode); break; case "after": _insertAfter(node, refNode); break; case "replace": refNode.parentNode.replaceChild(node, refNode); break; case "only": exports.empty(refNode); refNode.appendChild(node); break; case "first": if(refNode.firstChild){ _insertBefore(node, refNode.firstChild); break; } // else fallthrough... default: // aka: last refNode.appendChild(node); } } return node; // DomNode }; var create = exports.create = function create(/*DOMNode|String*/ tag, /*Object*/ attrs, /*DOMNode|String?*/ refNode, /*String?*/ pos){ // summary: // Create an element, allowing for optional attribute decoration // and placement. // description: // A DOM Element creation function. A shorthand method for creating a node or // a fragment, and allowing for a convenient optional attribute setting step, // as well as an optional DOM placement reference. // // Attributes are set by passing the optional object through `dojo/dom-attr.set`. // See `dojo/dom-attr.set` for noted caveats and nuances, and API if applicable. // // Placement is done via `dojo/dom-construct.place`, assuming the new node to be // the action node, passing along the optional reference node and position. // tag: DOMNode|String // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), // or an existing DOM node to process. // attrs: Object // An object-hash of attributes to set on the newly created node. // Can be null, if you don't want to set any attributes/styles. // See: `dojo/dom-attr.set` for a description of available attributes. // refNode: DOMNode|String? // Optional reference node. Used by `dojo/dom-construct.place` to place the newly created // node somewhere in the dom relative to refNode. Can be a DomNode reference // or String ID of a node. // pos: String? // Optional positional reference. Defaults to "last" by way of `dojo/domConstruct.place`, // though can be set to "first","after","before","last", "replace" or "only" // to further control the placement of the new node relative to the refNode. // 'refNode' is required if a 'pos' is specified. // example: // Create a DIV: // | require(["dojo/dom-construct"], function(domConstruct){ // | var n = domConstruct.create("div"); // | }); // // example: // Create a DIV with content: // | require(["dojo/dom-construct"], function(domConstruct){ // | var n = domConstruct.create("div", { innerHTML:"<p>hi</p>" }); // | }); // // example: // Place a new DIV in the BODY, with no attributes set // | require(["dojo/dom-construct", "dojo/_base/window"], function(domConstruct, win){ // | var n = domConstruct.create("div", null, win.body()); // | }); // // example: // Create an UL, and populate it with LI's. Place the list as the first-child of a // node with id="someId": // | require(["dojo/dom-construct", "dojo/_base/array"], // | function(domConstruct, arrayUtil){ // | var ul = domConstruct.create("ul", null, "someId", "first"); // | var items = ["one", "two", "three", "four"]; // | arrayUtil.forEach(items, function(data){ // | domConstruct.create("li", { innerHTML: data }, ul); // | }); // | }); // // example: // Create an anchor, with an href. Place in BODY: // | require(["dojo/dom-construct", "dojo/_base/window"], function(domConstruct, win){ // | domConstruct.create("a", { href:"foo.html", title:"Goto FOO!" }, win.body()); // | }); var doc = win.doc; if(refNode){ refNode = dom.byId(refNode); doc = refNode.ownerDocument; } if(typeof tag == "string"){ // inline'd type check tag = doc.createElement(tag); } if(attrs){ attr.set(tag, attrs); } if(refNode){ exports.place(tag, refNode, pos); } return tag; // DomNode }; function _empty(/*DomNode*/ node){ // TODO: remove this if() block in 2.0 when we no longer have to worry about IE memory leaks, // and then uncomment the emptyGrandchildren() test case from html.html. // Note that besides fixing #16957, using removeChild() is actually faster than setting node.innerHTML, // see http://jsperf.com/clear-dom-node. if("innerHTML" in node){ try{ // fast path node.innerHTML = ""; return; }catch(e){ // innerHTML is readOnly (e.g. TABLE (sub)elements in quirks mode) // Fall through (saves bytes) } } // SVG/strict elements don't support innerHTML for(var c; c = node.lastChild;){ // intentional assignment node.removeChild(c); } } exports.empty = function empty(/*DOMNode|String*/ node){ // summary: // safely removes all children of the node. // node: DOMNode|String // a reference to a DOM node or an id. // example: // Destroy node's children byId: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.empty("someId"); // | }); _empty(dom.byId(node)); }; function _destroy(/*DomNode*/ node, /*DomNode*/ parent){ // in IE quirks, node.canHaveChildren can be false but firstChild can be non-null (OBJECT/APPLET) if(node.firstChild){ _empty(node); } if(parent){ // removeNode(false) doesn't leak in IE 6+, but removeChild() and removeNode(true) are known to leak under IE 8- while 9+ is TBD. // In IE quirks mode, PARAM nodes as children of OBJECT/APPLET nodes have a removeNode method that does nothing and // the parent node has canHaveChildren=false even though removeChild correctly removes the PARAM children. // In IE, SVG/strict nodes don't have a removeNode method nor a canHaveChildren boolean. has("ie") && parent.canHaveChildren && "removeNode" in node ? node.removeNode(false) : parent.removeChild(node); } } var destroy = exports.destroy = function destroy(/*DOMNode|String*/ node){ // summary: // Removes a node from its parent, clobbering it and all of its // children. // // description: // Removes a node from its parent, clobbering it and all of its // children. Function only works with DomNodes, and returns nothing. // // node: DOMNode|String // A String ID or DomNode reference of the element to be destroyed // // example: // Destroy a node byId: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.destroy("someId"); // | }); node = dom.byId(node); if(!node){ return; } _destroy(node, node.parentNode); }; });