if (!dojo._hasResource["dojox.dtl.html"]) { // _hasResource checks added by
// build. Do not use _hasResource
// directly in your code.
dojo._hasResource["dojox.dtl.html"] = true;
dojo.provide("dojox.dtl.html");
dojo.require("dojox.dtl._base");
dojox.dtl.ObjectMap = function() {
this.contents = [];
}
dojo.extend(dojox.dtl.ObjectMap, {
get : function(key) {
var contents = this.contents;
for (var i = 0, content; content = contents[i]; i++) {
if (content[0] === key) {
return content[1];
}
}
},
put : function(key, value) {
var contents = this.contents;
for (var i = 0, content; content = contents[i]; i++) {
if (content[0] === key) {
if (arguments.length == 1) {
contents.splice(i, 1);
return;
}
content[1] = value;
return;
}
}
contents.push([key, value]);
},
toString : function() {
return "dojox.dtl.ObjectMap";
}
});
dojox.dtl.html = {
types : dojo.mixin({
change : -11,
attr : -12,
elem : 1,
text : 3
}, dojox.dtl.text.types),
_attributes : {},
_re : /(^\s+|\s+$)/g,
_re2 : /\b([a-zA-Z]+)="/g,
_re3 : //g,
_re4 : /^function anonymous\(\)\s*{\s*(.*)\s*}$/,
_trim : function(/* String */str) {
return str.replace(this._re, "");
},
getTemplate : function(text) {
if (typeof this._commentable == "undefined") {
// Check to see if the browser can handle comments
this._commentable = false;
var div = document.createElement("div");
div.innerHTML = "";
if (div.childNodes.length && div.childNodes[0].nodeType == 8
&& div.childNodes[0].data == "comment") {
this._commentable = true;
}
}
if (!this._commentable) {
// Strip comments
text = text.replace(this._re3, "$1");
}
var match;
while (match = this._re2.exec(text)) {
this._attributes[match[1]] = true;
}
var div = document.createElement("div");
div.innerHTML = text;
var output = {
pres : [],
posts : []
}
while (div.childNodes.length) {
if (!output.node && div.childNodes[0].nodeType == 1) {
output.node = div.removeChild(div.childNodes[0]);
} else if (!output.node) {
output.pres.push(div.removeChild(div.childNodes[0]));
} else {
output.posts.push(div.removeChild(div.childNodes[0]));
}
}
if (!output.node) {
throw new Error("Template did not provide any content");
}
return output;
},
tokenize : function(/* Node */node, /* Array? */tokens, /* Array? */
preNodes, /* Array? */postNodes) {
tokens = tokens || [];
var first = !tokens.length;
var types = this.types;
var children = [];
for (var i = 0, child; child = node.childNodes[i]; i++) {
children.push(child);
}
if (preNodes) {
for (var i = 0, child; child = preNodes[i]; i++) {
this._tokenize(node, child, tokens);
}
}
tokens.push([types.elem, node]);
tokens.push([types.change, node]);
for (var key in this._attributes) {
var value = "";
if (key == "class") {
value = node.className || value;
} else if (key == "for") {
value = node.htmlFor || value;
} else if (node.getAttribute) {
value = node.getAttribute(key, 2) || value;
if (key == "href" || key == "src") {
if (dojo.isIE) {
var hash = location.href.lastIndexOf(location.hash);
var href = location.href.substring(0, hash)
.split("/");
href.pop();
href = href.join("/") + "/";
if (value.indexOf(href) == 0) {
value = value.replace(href, "");
}
value = value.replace(/%20/g, " ").replace(/%7B/g,
"{").replace(/%7D/g, "}").replace(/%25/g,
"%");
}
if (value.indexOf("{%") != -1
|| value.indexOf("{{") != -1) {
node.setAttribute(key, "");
}
}
}
if (typeof value == "function") {
value = value.toString().replace(this._re4, "$1");
}
if (typeof value == "string"
&& (value.indexOf("{%") != -1
|| value.indexOf("{{") != -1 || (value && dojox.dtl.text
.getTag("attr:" + key, true)))) {
tokens.push([types.attr, node, key, value]);
}
}
if (!children.length) {
tokens.push([types.change, node.parentNode, true]);
if (postNodes) {
for (var i = 0, child; child = postNodes[i]; i++) {
this._tokenize(node, child, tokens);
}
}
return tokens;
}
for (var i = 0, child; child = children[i]; i++) {
this._tokenize(node, child, tokens);
}
if (node.parentNode && node.parentNode.tagName) {
tokens.push([types.change, node.parentNode, true]);
node.parentNode.removeChild(node);
}
if (postNodes) {
for (var i = 0, child; child = postNodes[i]; i++) {
this._tokenize(node, child, tokens);
}
}
if (first) {
tokens.push([types.change, node, true]);
}
return tokens;
},
_tokenize : function(parent, child, tokens) {
var types = this.types;
var data = child.data;
switch (child.nodeType) {
case 1 :
this.tokenize(child, tokens);
break;
case 3 :
if (data.match(/[^\s\n]/)) {
if (data.indexOf("{{") != -1
|| data.indexOf("{%") != -1) {
var texts = dojox.dtl.text.tokenize(data);
for (var j = 0, text; text = texts[j]; j++) {
if (typeof text == "string") {
tokens.push([types.text, text]);
} else {
tokens.push(text);
}
}
} else {
tokens.push([child.nodeType, child]);
}
}
if (child.parentNode)
child.parentNode.removeChild(child);
break;
case 8 :
if (data.indexOf("{%") == 0) {
tokens
.push([
types.tag,
this._trim(data.substring(2,
data.length - 3))]);
}
if (data.indexOf("{{") == 0) {
tokens
.push([
types.varr,
this._trim(data.substring(2,
data.length - 3))]);
}
if (child.parentNode)
child.parentNode.removeChild(child);
break;
}
}
}
dojox.dtl.HtmlTemplate = function(/* String|dojo._Url */obj) {
// summary: Use this object for HTML templating
var dd = dojox.dtl;
var ddh = dd.html;
if (!obj.node) {
if (typeof obj == "object") {
obj = dojox.dtl.text.getTemplateString(obj);
}
obj = ddh.getTemplate(obj);
}
var tokens = ddh.tokenize(obj.node, [], obj.pres, obj.posts);
var parser = new dd.HtmlParser(tokens);
this.nodelist = parser.parse();
}
dojo.extend(dojox.dtl.HtmlTemplate, {
_count : 0,
_re : /\bdojo:([a-zA-Z0-9_]+)\b/g,
setClass : function(str) {
this.getRootNode().className = str;
},
getRootNode : function() {
return this.rootNode;
},
getBuffer : function() {
return new dojox.dtl.HtmlBuffer();
},
render : function(context, buffer) {
buffer = buffer || this.getBuffer();
this.rootNode = null;
var onSetParent = dojo.connect(buffer, "onSetParent", this,
function(node) {
if (!this.rootNode) {
this.rootNode = node || true;
}
});
var output = this.nodelist.render(context
|| new dojox.dtl.Context({}), buffer);
dojo.disconnect(onSetParent);
buffer._flushCache();
return output;
},
unrender : function(context, buffer) {
return this.nodelist.unrender(context, buffer);
},
toString : function() {
return "dojox.dtl.HtmlTemplate";
}
});
dojox.dtl.HtmlBuffer = function(/* Node */parent) {
// summary: Allows the manipulation of DOM
// description:
// Use this to append a child, change the parent, or
// change the attribute of the current node.
this._parent = parent;
this._cache = [];
}
dojo.extend(dojox.dtl.HtmlBuffer, {
concat : function(/* DOMNode */node) {
if (!this._parent)
return this;
if (node.nodeType) {
var caches = this._getCache(this._parent);
if (node.parentNode === this._parent) {
// If we reach a node that already existed, fill in the
// cache for this same parent
var i = 0;
for (var i = 0, cache; cache = caches[i]; i++) {
this.onAddNode(node);
this._parent.insertBefore(cache, node);
}
caches.length = 0;
}
if (!node.parentNode || !node.parentNode.tagName) {
if (!this._parent.childNodes.length) {
this.onAddNode(node);
this._parent.appendChild(node);
} else {
caches.push(node);
}
}
}
return this;
},
remove : function(obj) {
if (typeof obj == "string") {
this._parent.removeAttribute(obj);
} else {
if (obj.parentNode === this._parent) {
this.onRemoveNode();
this._parent.removeChild(obj);
}
}
return this;
},
setAttribute : function(key, value) {
if (key == "class") {
this._parent.className = value;
} else if (key == "for") {
this._parent.htmlFor = value;
} else if (this._parent.setAttribute) {
this._parent.setAttribute(key, value);
}
return this;
},
setParent : function(node, /* Boolean? */up) {
if (!this._parent)
this._parent = node;
var caches = this._getCache(this._parent);
if (caches && caches.length && up) {
for (var i = 0, cache; cache = caches[i]; i++) {
if (cache !== this._parent
&& (!cache.parentNode || !cache.parentNode.tagName)) {
this.onAddNode(cache);
this._parent.appendChild(cache);
}
}
caches.length = 0;
}
this.onSetParent(node, up);
this._parent = node;
return this;
},
getParent : function() {
return this._parent;
},
onSetParent : function() {
// summary: Stub called when setParent is used.
},
onAddNode : function() {
// summary: Stub called when new nodes are added
},
onRemoveNode : function() {
// summary: Stub called when nodes are removed
},
_getCache : function(node) {
for (var i = 0, cache; cache = this._cache[i]; i++) {
if (cache[0] === node) {
return cache[1];
}
}
var arr = [];
this._cache.push([node, arr]);
return arr;
},
_flushCache : function(node) {
for (var i = 0, cache; cache = this._cache[i]; i++) {
if (!cache[1].length) {
this._cache.splice(i--, 1);
}
}
},
toString : function() {
return "dojox.dtl.HtmlBuffer";
}
});
dojox.dtl.HtmlNode = function(node) {
// summary: Places a node into DOM
this.contents = node;
}
dojo.extend(dojox.dtl.HtmlNode, {
render : function(context, buffer) {
return buffer.concat(this.contents);
},
unrender : function(context, buffer) {
return buffer.remove(this.contents);
},
clone : function(buffer) {
return new dojox.dtl.HtmlNode(this.contents);
},
toString : function() {
return "dojox.dtl.HtmlNode";
}
});
dojox.dtl.HtmlNodeList = function(/* Node[] */nodes) {
// summary: A list of any HTML-specific node object
// description:
// Any object that's used in the constructor or added
// through the push function much implement the
// render, unrender, and clone functions.
this.contents = nodes || [];
}
dojo.extend(dojox.dtl.HtmlNodeList, {
parents : new dojox.dtl.ObjectMap(),
push : function(node) {
this.contents.push(node);
},
unshift : function(node) {
this.contents.unshift(node);
},
render : function(context, buffer, /* Node */instance) {
if (instance) {
var parent = buffer.getParent();
}
for (var i = 0; i < this.contents.length; i++) {
buffer = this.contents[i].render(context, buffer);
if (!buffer)
throw new Error("Template node render functions must return their buffer");
}
if (parent) {
buffer.setParent(parent, true);
}
return buffer;
},
unrender : function(context, buffer) {
for (var i = 0; i < this.contents.length; i++) {
buffer = this.contents[i].unrender(context, buffer);
if (!buffer)
throw new Error("Template node render functions must return their buffer");
}
return buffer;
},
clone : function(buffer) {
// summary:
// Used to create an identical copy of a NodeList, useful for things
// like the for tag.
var dd = dojox.dtl;
var ddh = dd.html;
var parent = buffer.getParent();
var contents = this.contents;
var nodelist = new dd.HtmlNodeList();
var cloned = [];
for (var i = 0; i < contents.length; i++) {
var clone = contents[i].clone(buffer);
if (clone instanceof dd.ChangeNode
|| clone instanceof dd.HtmlNode) {
var item = this.parents.get(clone.contents);
if (item) {
clone.contents = item;
} else if (parent !== clone.contents
&& clone instanceof dd.HtmlNode) {
var node = clone.contents;
clone.contents = clone.contents.cloneNode(false);
cloned.push(node);
this.parents.put(node, clone.contents);
}
}
nodelist.push(clone);
}
for (var i = 0, clone; clone = cloned[i]; i++) {
this.parents.put(clone);
}
return nodelist;
},
toString : function() {
return "dojox.dtl.HtmlNodeList";
}
});
dojox.dtl.HtmlVarNode = function(str) {
// summary: A node to be processed as a variable
// description:
// Will render an object that supports the render function
// and the getRootNode function
this.contents = new dojox.dtl.Filter(str);
this._lists = {};
}
dojo.extend(dojox.dtl.HtmlVarNode, {
render : function(context, buffer) {
this._rendered = true;
var dd = dojox.dtl;
var ddh = dd.html;
var str = this.contents.resolve(context);
if (str && str.render && str.getRootNode) {
var root = this._curr = str.getRootNode();
var lists = this._lists;
var list = lists[root];
if (!list) {
list = lists[root] = new dd.HtmlNodeList();
list.push(new dd.ChangeNode(buffer.getParent()));
list.push(new dd.HtmlNode(root));
list.push(str);
list.push(new dd.ChangeNode(buffer.getParent(),
true));
}
return list.render(context, buffer);
} else {
if (!this._txt)
this._txt = document.createTextNode(str);
if (this._txt.data != str)
this._txt.data = str;
return buffer.concat(this._txt);
}
return buffer;
},
unrender : function(context, buffer) {
if (this._rendered) {
this._rendered = false;
if (this._curr) {
return this._lists[this._curr].unrender(context,
buffer);
} else if (this._txt) {
return buffer.remove(this._txt);
}
}
return buffer;
},
clone : function() {
return new dojox.dtl.HtmlVarNode(this.contents.contents);
},
toString : function() {
return "dojox.dtl.HtmlVarNode";
}
});
dojox.dtl.ChangeNode = function(node, /* Boolean? */up) {
// summary: Changes the parent during render/unrender
this.contents = node;
this._up = up;
}
dojo.extend(dojox.dtl.ChangeNode, {
render : function(context, buffer) {
return buffer.setParent(this.contents, this._up);
},
unrender : function(context, buffer) {
return buffer.setParent(this.contents);
},
clone : function(buffer) {
return new dojox.dtl.ChangeNode(this.contents, this._up);
},
toString : function() {
return "dojox.dtl.ChangeNode";
}
});
dojox.dtl.AttributeNode = function(key, value) {
// summary: Works on attributes
this._key = key;
this._value = value;
this._tpl = new dojox.dtl.Template(value);
this.contents = "";
}
dojo.extend(dojox.dtl.AttributeNode, {
render : function(context, buffer) {
var key = this._key;
var value = this._tpl.render(context);
if (this._rendered) {
if (value != this.contents) {
this.contents = value;
return buffer.setAttribute(key, value);
}
} else {
this._rendered = true;
this.contents = value;
return buffer.setAttribute(key, value);
}
return buffer;
},
unrender : function(context, buffer) {
if (this._rendered) {
this._rendered = false;
this.contents = "";
return buffer.remove(this.contents);
}
return buffer;
},
clone : function() {
return new dojox.dtl.AttributeNode(this._key, this._value);
},
toString : function() {
return "dojox.dtl.AttributeNode";
}
});
dojox.dtl.HtmlTextNode = function(str) {
// summary: Adds a straight text node without any processing
this.contents = document.createTextNode(str);
}
dojo.extend(dojox.dtl.HtmlTextNode, {
render : function(context, buffer) {
return buffer.concat(this.contents);
},
unrender : function(context, buffer) {
return buffer.remove(this.contents);
},
clone : function() {
return new dojox.dtl.HtmlTextNode(this.contents.data);
},
toString : function() {
return "dojox.dtl.HtmlTextNode";
}
});
dojox.dtl.HtmlParser = function(tokens) {
// summary: Turn a simple array into a set of objects
// description:
// This is also used by all tags to move through
// the list of nodes.
this.contents = tokens;
}
dojo.extend(dojox.dtl.HtmlParser, {
parse : function(/* Array? */stop_at) {
var dd = dojox.dtl;
var ddh = dd.html;
var types = ddh.types;
var terminators = {};
var tokens = this.contents;
if (!stop_at) {
stop_at = [];
}
for (var i = 0; i < stop_at.length; i++) {
terminators[stop_at[i]] = true;
}
var nodelist = new dd.HtmlNodeList();
while (tokens.length) {
var token = tokens.shift();
var type = token[0];
var value = token[1];
if (type == types.change) {
nodelist.push(new dd.ChangeNode(value, token[2]));
} else if (type == types.attr) {
var fn = dojox.dtl.text.getTag("attr:" + token[2], true);
if (fn) {
nodelist.push(fn(null, token[2] + " " + token[3]));
} else {
nodelist.push(new dd.AttributeNode(token[2], token[3]));
}
} else if (type == types.elem) {
var fn = dojox.dtl.text.getTag("node:"
+ value.tagName.toLowerCase(), true);
if (fn) {
// TODO: We need to move this to tokenization so that
// it's before the
// node and the parser can be passed here instead of
// null
nodelist.push(fn(null, value, value.tagName
.toLowerCase()));
}
nodelist.push(new dd.HtmlNode(value));
} else if (type == types.varr) {
nodelist.push(new dd.HtmlVarNode(value));
} else if (type == types.text) {
nodelist.push(new dd.HtmlTextNode(value.data || value));
} else if (type == types.tag) {
if (terminators[value]) {
tokens.unshift(token);
return nodelist;
}
var cmd = value.split(/\s+/g);
if (cmd.length) {
cmd = cmd[0];
var fn = dojox.dtl.text.getTag(cmd);
if (typeof fn != "function") {
throw new Error("Function not found for ", cmd);
}
var tpl = fn(this, value);
if (tpl) {
nodelist.push(tpl);
}
}
}
}
if (stop_at.length) {
throw new Error("Could not find closing tag(s): "
+ stop_at.toString());
}
return nodelist;
},
next : function() {
// summary: Used by tags to discover what token was found
var token = this.contents.shift();
return {
type : token[0],
text : token[1]
};
},
skipPast : function(endtag) {
return dojox.dtl.Parser.prototype.skipPast.call(this, endtag);
},
getVarNode : function() {
return dojox.dtl.HtmlVarNode;
},
getTextNode : function() {
return dojox.dtl.HtmlTextNode;
},
getTemplate : function(/* String */loc) {
return new dojox.dtl.HtmlTemplate(dojox.dtl.html.getTemplate(loc));
},
toString : function() {
return "dojox.dtl.HtmlParser";
}
});
dojox.dtl.register.tag("dojox.dtl.tag.event", "dojox.dtl.tag.event", [[
/(attr:)?on(click|key(up))/i, "on"]]);
dojox.dtl.register.tag("dojox.dtl.tag.html", "dojox.dtl.tag.html", ["html",
"attr:attach", "attr:tstyle"]);
}