e44a7e37b6
git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f
570 lines
15 KiB
JavaScript
570 lines
15 KiB
JavaScript
if(!dojo._hasResource["dojox.dtl._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
dojo._hasResource["dojox.dtl._base"] = true;
|
|
dojo.provide("dojox.dtl._base");
|
|
|
|
dojo.require("dojox.string.Builder");
|
|
dojo.require("dojox.string.tokenize");
|
|
|
|
(function(){
|
|
var dd = dojox.dtl;
|
|
|
|
dd._Context = dojo.extend(function(dict){
|
|
// summary: Pass one of these when rendering a template to tell the template what values to use.
|
|
dojo.mixin(this, dict || {});
|
|
this._dicts = [];
|
|
},
|
|
{
|
|
push: function(){
|
|
var dict = {};
|
|
var keys = this.getKeys();
|
|
for(var i = 0, key; key = keys[i]; i++){
|
|
dict[key] = this[key];
|
|
delete this[key];
|
|
}
|
|
this._dicts.unshift(dict);
|
|
},
|
|
pop: function(){
|
|
if(!this._dicts.length){
|
|
throw new Error("pop() called on empty Context");
|
|
}
|
|
var dict = this._dicts.shift();
|
|
dojo.mixin(this, dict);
|
|
},
|
|
getKeys: function(){
|
|
var keys = [];
|
|
for(var key in this){
|
|
if(this.hasOwnProperty(key) && key != "_dicts" && key != "_this"){
|
|
keys.push(key);
|
|
}
|
|
}
|
|
return keys;
|
|
},
|
|
get: function(key, otherwise){
|
|
if(typeof this[key] != "undefined"){
|
|
return this._normalize(this[key]);
|
|
}
|
|
|
|
for(var i = 0, dict; dict = this._dicts[i]; i++){
|
|
if(typeof dict[key] != "undefined"){
|
|
return this._normalize(dict[key]);
|
|
}
|
|
}
|
|
|
|
return otherwise;
|
|
},
|
|
_normalize: function(value){
|
|
if(value instanceof Date){
|
|
value.year = value.getFullYear();
|
|
value.month = value.getMonth() + 1;
|
|
value.day = value.getDate();
|
|
value.date = value.year + "-" + ("0" + value.month).slice(-2) + "-" + ("0" + value.day).slice(-2);
|
|
value.hour = value.getHours();
|
|
value.minute = value.getMinutes();
|
|
value.second = value.getSeconds();
|
|
value.microsecond = value.getMilliseconds();
|
|
}
|
|
return value;
|
|
},
|
|
update: function(dict){
|
|
this.push();
|
|
if(dict){
|
|
dojo.mixin(this, dict);
|
|
}
|
|
}
|
|
});
|
|
|
|
var ddt = dd.text = {
|
|
types: {tag: -1, varr: -2, text: 3},
|
|
pySplit: function(str){
|
|
// summary: Split a string according to Python's split function
|
|
str = dojo.trim(str);
|
|
return (!str.length) ? [] : str.split(/\s+/g);
|
|
},
|
|
_get: function(module, name, errorless){
|
|
// summary: Used to find both tags and filters
|
|
var params = dd.register.get(module, name.toLowerCase(), errorless);
|
|
if(!params){
|
|
if(!errorless){
|
|
throw new Error("No tag found for " + name);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var fn = params[1];
|
|
var require = params[2];
|
|
|
|
var parts;
|
|
if(fn.indexOf(":") != -1){
|
|
parts = fn.split(":");
|
|
fn = parts.pop();
|
|
}
|
|
|
|
dojo["require"](require);
|
|
|
|
var parent = dojo.getObject(require);
|
|
|
|
return parent[fn || name] || parent[name + "_"];
|
|
},
|
|
getTag: function(name, errorless){
|
|
return ddt._get("tag", name, errorless);
|
|
},
|
|
getFilter: function(name, errorless){
|
|
return ddt._get("filter", name, errorless);
|
|
},
|
|
getTemplate: function(file){
|
|
return new dd.Template(dd.getTemplateString(file));
|
|
},
|
|
getTemplateString: function(file){
|
|
return dojo._getText(file.toString()) || "";
|
|
},
|
|
_resolveLazy: function(location, sync, json){
|
|
if(sync){
|
|
if(json){
|
|
return dojo.fromJson(dojo._getText(location)) || {};
|
|
}else{
|
|
return dd.text.getTemplateString(location);
|
|
}
|
|
}else{
|
|
return dojo.xhrGet({
|
|
handleAs: (json) ? "json" : "text",
|
|
url: location
|
|
});
|
|
}
|
|
},
|
|
_resolveTemplateArg: function(arg, sync){
|
|
if(ddt._isTemplate(arg)){
|
|
if(!sync){
|
|
var d = new dojo.Deferred();
|
|
d.callback(arg);
|
|
return d;
|
|
}
|
|
return arg;
|
|
}
|
|
return ddt._resolveLazy(arg, sync);
|
|
},
|
|
_isTemplate: function(arg){
|
|
return (typeof arg == "undefined") || (dojo.isString(arg) && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
|
|
},
|
|
_resolveContextArg: function(arg, sync){
|
|
if(arg.constructor == Object){
|
|
if(!sync){
|
|
var d = new dojo.Deferred;
|
|
d.callback(arg);
|
|
return d;
|
|
}
|
|
return arg;
|
|
}
|
|
return ddt._resolveLazy(arg, sync, true);
|
|
},
|
|
_re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
|
|
tokenize: function(str){
|
|
return dojox.string.tokenize(str, ddt._re, ddt._parseDelims);
|
|
},
|
|
_parseDelims: function(varr, load, tag){
|
|
var types = ddt.types;
|
|
if(varr){
|
|
return [types.varr, varr];
|
|
}else if(load){
|
|
var parts = dd.text.pySplit(tag);
|
|
for(var i = 0, part; part = parts[i]; i++){
|
|
dojo["require"](part);
|
|
}
|
|
}else{
|
|
return [types.tag, tag];
|
|
}
|
|
}
|
|
}
|
|
|
|
dd.Template = dojo.extend(function(/*String|dojo._Url*/ template){
|
|
// template:
|
|
// The string or location of the string to
|
|
// use as a template
|
|
var str = ddt._resolveTemplateArg(template, true) || "";
|
|
var tokens = ddt.tokenize(str);
|
|
var parser = new dd._Parser(tokens);
|
|
this.nodelist = parser.parse();
|
|
},
|
|
{
|
|
update: function(node, context){
|
|
// node: DOMNode|String|dojo.NodeList
|
|
// A node reference or set of nodes
|
|
// context: dojo._Url|String|Object
|
|
// The context object or location
|
|
return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
|
|
var content = this.render(new dd._Context(contextObject));
|
|
if(node.forEach){
|
|
node.forEach(function(item){
|
|
item.innerHTML = content;
|
|
});
|
|
}else{
|
|
dojo.byId(node).innerHTML = content;
|
|
}
|
|
return this;
|
|
});
|
|
},
|
|
render: function(context, /*concatenatable?*/ buffer){
|
|
buffer = buffer || this.getBuffer();
|
|
context = context || new dd._Context({});
|
|
return this.nodelist.render(context, buffer) + "";
|
|
},
|
|
getBuffer: function(){
|
|
dojo.require("dojox.string.Builder");
|
|
return new dojox.string.Builder();
|
|
}
|
|
});
|
|
|
|
dd._Filter = dojo.extend(function(token){
|
|
// summary: Uses a string to find (and manipulate) a variable
|
|
if(!token) throw new Error("Filter must be called with variable name");
|
|
this.contents = token;
|
|
|
|
var cache = this._cache[token];
|
|
if(cache){
|
|
this.key = cache[0];
|
|
this.filters = cache[1];
|
|
}else{
|
|
this.filters = [];
|
|
dojox.string.tokenize(token, this._re, this._tokenize, this);
|
|
this._cache[token] = [this.key, this.filters];
|
|
}
|
|
},
|
|
{
|
|
_cache: {},
|
|
_re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
|
|
_values: {
|
|
0: '"', // _("text")
|
|
1: '"', // "text"
|
|
2: "", // variable
|
|
8: '"' // 'text'
|
|
},
|
|
_args: {
|
|
4: '"', // :_("text")
|
|
5: '"', // :"text"
|
|
6: "", // :variable
|
|
7: "'"// :'text'
|
|
},
|
|
_tokenize: function(){
|
|
var pos, arg;
|
|
|
|
for(var i = 0, has = []; i < arguments.length; i++){
|
|
has[i] = (typeof arguments[i] != "undefined" && dojo.isString(arguments[i]) && arguments[i]);
|
|
}
|
|
|
|
if(!this.key){
|
|
for(pos in this._values){
|
|
if(has[pos]){
|
|
this.key = this._values[pos] + arguments[pos] + this._values[pos];
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
for(pos in this._args){
|
|
if(has[pos]){
|
|
var value = arguments[pos];
|
|
if(this._args[pos] == "'"){
|
|
value = value.replace(/\\'/g, "'");
|
|
}else if(this._args[pos] == '"'){
|
|
value = value.replace(/\\"/g, '"');
|
|
}
|
|
arg = [!this._args[pos], value];
|
|
break;
|
|
}
|
|
}
|
|
// Get a named filter
|
|
var fn = ddt.getFilter(arguments[3]);
|
|
if(!dojo.isFunction(fn)) throw new Error(arguments[3] + " is not registered as a filter");
|
|
this.filters.push([fn, arg]);
|
|
}
|
|
},
|
|
getExpression: function(){
|
|
return this.contents;
|
|
},
|
|
resolve: function(context){
|
|
var str = this.resolvePath(this.key, context);
|
|
for(var i = 0, filter; filter = this.filters[i]; i++){
|
|
// Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
|
|
// and [1][1] is either the variable name of the string content.
|
|
if(filter[1]){
|
|
if(filter[1][0]){
|
|
str = filter[0](str, this.resolvePath(filter[1][1], context));
|
|
}else{
|
|
str = filter[0](str, filter[1][1]);
|
|
}
|
|
}else{
|
|
str = filter[0](str);
|
|
}
|
|
}
|
|
return str;
|
|
},
|
|
resolvePath: function(path, context){
|
|
var current, parts;
|
|
var first = path.charAt(0);
|
|
var last = path.slice(-1);
|
|
if(!isNaN(parseInt(first))){
|
|
current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
|
|
}else if(first == '"' && first == last){
|
|
current = path.slice(1, -1);
|
|
}else{
|
|
if(path == "true"){ return true; }
|
|
if(path == "false"){ return false; }
|
|
if(path == "null" || path == "None"){ return null; }
|
|
parts = path.split(".");
|
|
current = context.get(parts[0]);
|
|
for(var i = 1; i < parts.length; i++){
|
|
var part = parts[i];
|
|
if(current){
|
|
if(dojo.isObject(current) && part == "items" && typeof current[part] == "undefined"){
|
|
var items = [];
|
|
for(var key in current){
|
|
items.push([key, current[key]]);
|
|
}
|
|
current = items;
|
|
continue;
|
|
}
|
|
|
|
if(current.get && dojo.isFunction(current.get)){
|
|
current = current.get(part);
|
|
}else if(typeof current[part] == "undefined"){
|
|
current = current[part];
|
|
break;
|
|
}else{
|
|
current = current[part];
|
|
}
|
|
|
|
if(dojo.isFunction(current)){
|
|
if(current.alters_data){
|
|
current = "";
|
|
}else{
|
|
current = current();
|
|
}
|
|
}
|
|
}else{
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
return current;
|
|
}
|
|
});
|
|
|
|
dd._TextNode = dd._Node = dojo.extend(function(/*Object*/ obj){
|
|
// summary: Basic catch-all node
|
|
this.contents = obj;
|
|
},
|
|
{
|
|
set: function(data){
|
|
this.contents = data;
|
|
},
|
|
render: function(context, buffer){
|
|
// summary: Adds content onto the buffer
|
|
return buffer.concat(this.contents);
|
|
}
|
|
});
|
|
|
|
dd._NodeList = dojo.extend(function(/*Node[]*/ nodes){
|
|
// summary: Allows us to render a group of nodes
|
|
this.contents = nodes || [];
|
|
this.last = "";
|
|
},
|
|
{
|
|
push: function(node){
|
|
// summary: Add a new node to the list
|
|
this.contents.push(node);
|
|
},
|
|
render: function(context, buffer){
|
|
// summary: Adds all content onto the buffer
|
|
for(var i = 0; i < this.contents.length; i++){
|
|
buffer = this.contents[i].render(context, buffer);
|
|
if(!buffer) throw new Error("Template must return buffer");
|
|
}
|
|
return buffer;
|
|
},
|
|
dummyRender: function(context){
|
|
return this.render(context, dd.Template.prototype.getBuffer()).toString();
|
|
},
|
|
unrender: function(){ return arguments[1]; },
|
|
clone: function(){ return this; }
|
|
});
|
|
|
|
dd._VarNode = dojo.extend(function(str){
|
|
// summary: A node to be processed as a variable
|
|
this.contents = new dd._Filter(str);
|
|
},
|
|
{
|
|
render: function(context, buffer){
|
|
var str = this.contents.resolve(context);
|
|
return buffer.concat(str);
|
|
}
|
|
});
|
|
|
|
dd._noOpNode = new function(){
|
|
// summary: Adds a no-op node. Useful in custom tags
|
|
this.render = this.unrender = function(){ return arguments[1]; }
|
|
this.clone = function(){ return this; }
|
|
}
|
|
|
|
dd._Parser = dojo.extend(function(tokens){
|
|
// summary: Parser used during initialization and for tag groups.
|
|
this.contents = tokens;
|
|
},
|
|
{
|
|
i: 0,
|
|
parse: function(/*Array?*/ stop_at){
|
|
// summary: Turns tokens into nodes
|
|
// description: Steps into tags are they're found. Blocks use the parse object
|
|
// to find their closing tag (the stop_at array). stop_at is inclusive, it
|
|
// returns the node that matched.
|
|
var types = ddt.types;
|
|
var terminators = {};
|
|
stop_at = stop_at || [];
|
|
for(var i = 0; i < stop_at.length; i++){
|
|
terminators[stop_at[i]] = true;
|
|
}
|
|
|
|
var nodelist = new dd._NodeList();
|
|
while(this.i < this.contents.length){
|
|
token = this.contents[this.i++];
|
|
if(dojo.isString(token)){
|
|
nodelist.push(new dd._TextNode(token));
|
|
}else{
|
|
var type = token[0];
|
|
var text = token[1];
|
|
if(type == types.varr){
|
|
nodelist.push(new dd._VarNode(text));
|
|
}else if(type == types.tag){
|
|
if(terminators[text]){
|
|
--this.i;
|
|
return nodelist;
|
|
}
|
|
var cmd = text.split(/\s+/g);
|
|
if(cmd.length){
|
|
cmd = cmd[0];
|
|
var fn = ddt.getTag(cmd);
|
|
if(fn){
|
|
nodelist.push(fn(this, text));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(stop_at.length){
|
|
throw new Error("Could not find closing tag(s): " + stop_at.toString());
|
|
}
|
|
|
|
this.contents.length = 0;
|
|
return nodelist;
|
|
},
|
|
next: function(){
|
|
// summary: Returns the next token in the list.
|
|
var token = this.contents[this.i++];
|
|
return {type: token[0], text: token[1]};
|
|
},
|
|
skipPast: function(endtag){
|
|
var types = ddt.types;
|
|
while(this.i < this.contents.length){
|
|
var token = this.contents[this.i++];
|
|
if(token[0] == types.tag && token[1] == endtag){
|
|
return;
|
|
}
|
|
}
|
|
throw new Error("Unclosed tag found when looking for " + endtag);
|
|
},
|
|
getVarNodeConstructor: function(){
|
|
return dd._VarNode;
|
|
},
|
|
getTextNodeConstructor: function(){
|
|
return dd._TextNode;
|
|
},
|
|
getTemplate: function(file){
|
|
return new dd.Template(file);
|
|
}
|
|
});
|
|
|
|
dd.register = {
|
|
_registry: {
|
|
attributes: [],
|
|
tags: [],
|
|
filters: []
|
|
},
|
|
get: function(/*String*/ module, /*String*/ name){
|
|
var registry = dd.register._registry[module + "s"];
|
|
for(var i = 0, entry; entry = registry[i]; i++){
|
|
if(dojo.isString(entry[0])){
|
|
if(entry[0] == name){
|
|
return entry;
|
|
}
|
|
}else if(name.match(entry[0])){
|
|
return entry;
|
|
}
|
|
}
|
|
},
|
|
getAttributeTags: function(){
|
|
var tags = [];
|
|
var registry = dd.register._registry.attributes;
|
|
for(var i = 0, entry; entry = registry[i]; i++){
|
|
if(entry.length == 3){
|
|
tags.push(entry);
|
|
}else{
|
|
var fn = dojo.getObject(entry[1]);
|
|
if(fn && dojo.isFunction(fn)){
|
|
entry.push(fn);
|
|
tags.push(entry);
|
|
}
|
|
}
|
|
}
|
|
return tags;
|
|
},
|
|
_any: function(type, base, locations){
|
|
for(var path in locations){
|
|
for(var i = 0, fn; fn = locations[path][i]; i++){
|
|
var key = fn;
|
|
if(dojo.isArray(fn)){
|
|
key = fn[0];
|
|
fn = fn[1];
|
|
}
|
|
if(dojo.isString(key)){
|
|
if(key.substr(0, 5) == "attr:"){
|
|
var attr = fn;
|
|
if(attr.substr(0, 5) == "attr:"){
|
|
attr = attr.slice(5);
|
|
}
|
|
dd.register._registry.attributes.push([attr, base + "." + path + "." + attr]);
|
|
}
|
|
key = key.toLowerCase();
|
|
}
|
|
dd.register._registry[type].push([
|
|
key,
|
|
fn,
|
|
base + "." + path
|
|
]);
|
|
}
|
|
}
|
|
},
|
|
tags: function(/*String*/ base, /*Object*/ locations){
|
|
dd.register._any("tags", base, locations);
|
|
},
|
|
filters: function(/*String*/ base, /*Object*/ locations){
|
|
dd.register._any("filters", base, locations);
|
|
}
|
|
}
|
|
|
|
dd.register.tags("dojox.dtl.tag", {
|
|
"date": ["now"],
|
|
"logic": ["if", "for", "ifequal", "ifnotequal"],
|
|
"loader": ["extends", "block", "include", "load", "ssi"],
|
|
"misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
|
|
"loop": ["cycle", "ifchanged", "regroup"]
|
|
});
|
|
dd.register.filters("dojox.dtl.filter", {
|
|
"dates": ["date", "time", "timesince", "timeuntil"],
|
|
"htmlstrings": ["escape", "linebreaks", "linebreaksbr", "removetags", "striptags"],
|
|
"integers": ["add", "get_digit"],
|
|
"lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
|
|
"logic": ["default", "default_if_none", "divisibleby", "yesno"],
|
|
"misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
|
|
"strings": ["addslashes", "capfirst", "center", "cut", "fix_ampersands", "floatformat", "iriencode", "linenumbers", "ljust", "lower", "make_list", "rjust", "slugify", "stringformat", "title", "truncatewords", "truncatewords_html", "upper", "urlencode", "urlize", "urlizetrunc", "wordcount", "wordwrap"]
|
|
});
|
|
})();
|
|
|
|
}
|