330 lines
10 KiB
JavaScript
330 lines
10 KiB
JavaScript
|
define("dojo/dom-construct", ["exports", "./_base/kernel", "./sniff", "./_base/window", "./dom", "./dom-attr", "./on"],
|
||
|
function(exports, dojo, has, win, dom, attr, on){
|
||
|
// 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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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.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:
|
||
|
// | var tr = dojo.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");
|
||
|
}
|
||
|
|
||
|
// 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(/*DOMNode|String*/ node, /*DOMNode|String*/ refNode, /*String|Number?*/ 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|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:
|
||
|
// | dojo.place("someNode", "anotherNode");
|
||
|
// example:
|
||
|
// Place a node by string id before another node by string id
|
||
|
// | dojo.place("someNode", "anotherNode", "before");
|
||
|
// example:
|
||
|
// Create a Node, and place it in the body element (last child):
|
||
|
// | dojo.place("<div></div>", dojo.body());
|
||
|
// example:
|
||
|
// Put a new LI as the first child of a list by id:
|
||
|
// | dojo.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
|
||
|
};
|
||
|
|
||
|
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.setAttr`.
|
||
|
// See `dojo.setAttr` for noted caveats and nuances, and API if applicable.
|
||
|
//
|
||
|
// Placement is done via `dojo.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.setAttr` for a description of available attributes.
|
||
|
// refNode: DOMNode|String?
|
||
|
// Optional reference node. Used by `dojo.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.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:
|
||
|
// | var n = dojo.create("div");
|
||
|
//
|
||
|
// example:
|
||
|
// Create a DIV with content:
|
||
|
// | var n = dojo.create("div", { innerHTML:"<p>hi</p>" });
|
||
|
//
|
||
|
// example:
|
||
|
// Place a new DIV in the BODY, with no attributes set
|
||
|
// | var n = dojo.create("div", null, dojo.body());
|
||
|
//
|
||
|
// example:
|
||
|
// Create an UL, and populate it with LI's. Place the list as the first-child of a
|
||
|
// node with id="someId":
|
||
|
// | var ul = dojo.create("ul", null, "someId", "first");
|
||
|
// | var items = ["one", "two", "three", "four"];
|
||
|
// | dojo.forEach(items, function(data){
|
||
|
// | dojo.create("li", { innerHTML: data }, ul);
|
||
|
// | });
|
||
|
//
|
||
|
// example:
|
||
|
// Create an anchor, with an href. Place in BODY:
|
||
|
// | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body());
|
||
|
//
|
||
|
// example:
|
||
|
// Create a `dojo/NodeList()` from a new element (for syntactic sugar):
|
||
|
// | dojo.query(dojo.create('div'))
|
||
|
// | .addClass("newDiv")
|
||
|
// | .onclick(function(e){ console.log('clicked', e.target) })
|
||
|
// | .place("#someNode"); // redundant, but cleaner.
|
||
|
|
||
|
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
|
||
|
};
|
||
|
|
||
|
var _empty = has("ie") ?
|
||
|
function(/*DomNode*/ node){
|
||
|
try{
|
||
|
node.innerHTML = ""; // really fast when it works
|
||
|
}catch(e){ // IE can generate Unknown Error
|
||
|
for(var c; c = node.lastChild;){ // intentional assignment
|
||
|
_destroy(c, node); // destroy is better than removeChild so TABLE elements are removed in proper order
|
||
|
}
|
||
|
}
|
||
|
} :
|
||
|
function(/*DomNode*/ node){
|
||
|
node.innerHTML = "";
|
||
|
};
|
||
|
|
||
|
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:
|
||
|
// | dojo.empty("someId");
|
||
|
//
|
||
|
// example:
|
||
|
// Destroy all nodes' children in a list by reference:
|
||
|
// | dojo.query(".someNode").forEach(dojo.empty);
|
||
|
|
||
|
_empty(dom.byId(node));
|
||
|
};
|
||
|
|
||
|
|
||
|
function _destroy(/*DomNode*/ node, /*DomNode*/ parent){
|
||
|
if(node.firstChild){
|
||
|
_empty(node);
|
||
|
}
|
||
|
if(parent){
|
||
|
parent.removeChild(node);
|
||
|
}
|
||
|
}
|
||
|
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:
|
||
|
// | dojo.destroy("someId");
|
||
|
//
|
||
|
// example:
|
||
|
// Destroy all nodes in a list by reference:
|
||
|
// | dojo.query(".someNode").forEach(dojo.destroy);
|
||
|
|
||
|
node = dom.byId(node);
|
||
|
if(!node){ return; }
|
||
|
_destroy(node, node.parentNode);
|
||
|
};
|
||
|
});
|