New file |
0,0 → 1,658 |
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 = "<!--Test comment handling, and long comments, using comments whenever possible.-->"; |
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"]); |
|
} |