Subversion Repositories Applications.papyrus

Compare Revisions

Ignore whitespace Rev 2149 → Rev 2150

/trunk/api/js/dojo1.0/dojox/dtl/html.js
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"]);
 
}