123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- 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);
- };
- });
|