//>>built define("dojox/dtl/_base", [ "dojo/_base/kernel", "dojo/_base/lang", "dojox/string/tokenize", "dojo/_base/json", "dojo/dom", "dojo/_base/xhr", "dojox/string/Builder", "dojo/_base/Deferred"], function(kernel, lang, Tokenize, json, dom, xhr, StringBuilder, deferred){ /*===== Tokenize = dojox.string.tokenize; StringBuilder = dojox.string.Builder; =====*/ kernel.experimental("dojox.dtl"); var dd = lang.getObject("dojox.dtl", true); dd._base = {}; /*===== dd = dojox.dtl; =====*/ dd.TOKEN_BLOCK = -1; dd.TOKEN_VAR = -2; dd.TOKEN_COMMENT = -3; dd.TOKEN_TEXT = 3; /*===== dd._Context = function(dict){ // summary: Pass one of these when rendering a template to tell the template what values to use. } =====*/ dd._Context = lang.extend(function(dict){ // summary: Pass one of these when rendering a template to tell the template what values to use. if(dict){ lang._mixin(this, dict); if(dict.get){ // Preserve passed getter and restore prototype get this._getter = dict.get; delete this.get; } } }, { push: function(){ var last = this; var context = lang.delegate(this); context.pop = function(){ return last; } return context; }, pop: function(){ throw new Error("pop() called on empty Context"); }, get: function(key, otherwise){ var n = this._normalize; if(this._getter){ var got = this._getter(key); if(got !== undefined){ return n(got); } } if(this[key] !== undefined){ return n(this[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){ var context = this.push(); if(dict){ lang._mixin(this, dict); } return context; } }); var smart_split_re = /("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+)/g; var split_re = /\s+/g; var split = function(/*String|RegExp?*/ splitter, /*Integer?*/ limit){ splitter = splitter || split_re; if(!(splitter instanceof RegExp)){ splitter = new RegExp(splitter, "g"); } if(!splitter.global){ throw new Error("You must use a globally flagged RegExp with split " + splitter); } splitter.exec(""); // Reset the global var part, parts = [], lastIndex = 0, i = 0; while((part = splitter.exec(this))){ parts.push(this.slice(lastIndex, splitter.lastIndex - part[0].length)); lastIndex = splitter.lastIndex; if(limit && (++i > limit - 1)){ break; } } parts.push(this.slice(lastIndex)); return parts; } dd.Token = function(token_type, contents){ this.token_type = token_type; this.contents = new String(lang.trim(contents)); this.contents.split = split; this.split = function(){ return String.prototype.split.apply(this.contents, arguments); } } dd.Token.prototype.split_contents = function(/*Integer?*/ limit){ var bit, bits = [], i = 0; limit = limit || 999; while(i++ < limit && (bit = smart_split_re.exec(this.contents))){ bit = bit[0]; if(bit.charAt(0) == '"' && bit.slice(-1) == '"'){ bits.push('"' + bit.slice(1, -1).replace('\\"', '"').replace('\\\\', '\\') + '"'); }else if(bit.charAt(0) == "'" && bit.slice(-1) == "'"){ bits.push("'" + bit.slice(1, -1).replace("\\'", "'").replace('\\\\', '\\') + "'"); }else{ bits.push(bit); } } return bits; } var ddt = dd.text = { _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 deps = params[2]; var parts; if(fn.indexOf(":") != -1){ parts = fn.split(":"); fn = parts.pop(); } // FIXME: THIS DESIGN DOES NOT WORK WITH ASYNC LOADERS! var mod = deps; if (/\./.test(deps)) { deps = deps.replace(/\./g, "/"); } require([deps], function(){}); var parent = lang.getObject(mod); return parent[fn || name] || parent[name + "_"] || parent[fn + "_"]; }, 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(ddt.getTemplateString(file)); }, getTemplateString: function(file){ return xhr._getText(file.toString()) || ""; }, _resolveLazy: function(location, sync, json){ if(sync){ if(json){ return json.fromJson(xhr._getText(location)) || {}; }else{ return dd.text.getTemplateString(location); } }else{ return xhr.get({ handleAs: json ? "json" : "text", url: location }); } }, _resolveTemplateArg: function(arg, sync){ if(ddt._isTemplate(arg)){ if(!sync){ var d = new deferred(); d.callback(arg); return d; } return arg; } return ddt._resolveLazy(arg, sync); }, _isTemplate: function(arg){ return (arg === undefined) || (typeof arg == "string" && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1)); }, _resolveContextArg: function(arg, sync){ if(arg.constructor == Object){ if(!sync){ var d = new 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 Tokenize(str, ddt._re, ddt._parseDelims); }, _parseDelims: function(varr, load, tag){ if(varr){ return [dd.TOKEN_VAR, varr]; }else if(load){ var parts = lang.trim(tag).split(/\s+/g); for(var i = 0, part; part = parts[i]; i++){ if (/\./.test(part)){ part = part.replace(/\./g,"/"); } require([part]); } }else{ return [dd.TOKEN_BLOCK, tag]; } } } /*===== dd.Template = function(template, isString){ // summary: // The base class for text-based templates. // template: String|dojo._Url // The string or location of the string to // use as a template // isString: Boolean // Indicates whether the template is a string or a url. }; dd.Template.prototype.update= function(node, context){ // summary: // Updates this template according to the given context. // node: DOMNode|String|dojo.NodeList // A node reference or set of nodes // context: dojo._Url|String|Object // The context object or location } dd.Template.prototype.render= function(context, buffer){ // summary: // Renders this template. // context: Object // The runtime context. // buffer: StringBuilder? // A string buffer. } =====*/ dd.Template = lang.extend(function(/*String|dojo._Url*/ template, /*Boolean*/ isString){ // template: // The string or location of the string to // use as a template var str = isString ? template : ddt._resolveTemplateArg(template, true) || ""; var tokens = ddt.tokenize(str); var parser = new dd._Parser(tokens); this.nodelist = parser.parse(); }, { update: function(node, context){ // summary: // Updates this template according to the given 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{ dom.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(){ return new StringBuilder(); } }); var qfRe = /\{\{\s*(.+?)\s*\}\}/g; dd.quickFilter = function(str){ if(!str){ return new dd._NodeList(); } if(str.indexOf("{%") == -1){ return new dd._QuickNodeList(Tokenize(str, qfRe, function(token){ return new dd._Filter(token); })); } } dd._QuickNodeList = lang.extend(function(contents){ this.contents = contents; }, { render: function(context, buffer){ for(var i = 0, l = this.contents.length; i < l; i++){ if(this.contents[i].resolve){ buffer = buffer.concat(this.contents[i].resolve(context)); }else{ buffer = buffer.concat(this.contents[i]); } } return buffer; }, dummyRender: function(context){ return this.render(context, dd.Template.prototype.getBuffer()).toString(); }, clone: function(buffer){ return this; } }); dd._Filter = lang.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 = []; 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] = (arguments[i] !== undefined && typeof arguments[i] == "string" && 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(!lang.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){ if(this.key === undefined){ return ""; } 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]); if(lang.isFunction(current)){ var self = context.getThis && context.getThis(); if(current.alters_data){ current = ""; }else if(self){ current = current.call(self); }else{ current = ""; } } for(var i = 1; i < parts.length; i++){ var part = parts[i]; if(current){ var base = current; if(lang.isObject(current) && part == "items" && current[part] === undefined){ var items = []; for(var key in current){ items.push([key, current[key]]); } current = items; continue; } if(current.get && lang.isFunction(current.get) && current.get.safe){ current = current.get(part); }else if(current[part] === undefined){ current = current[part]; break; }else{ current = current[part]; } if(lang.isFunction(current)){ if(current.alters_data){ current = ""; }else{ current = current.call(base); } }else if(current instanceof Date){ current = dd._Context.prototype._normalize(current); } }else{ return ""; } } } return current; } }); dd._TextNode = dd._Node = lang.extend(function(/*Object*/ obj){ // summary: Basic catch-all node this.contents = obj; }, { set: function(data){ this.contents = data; return this; }, render: function(context, buffer){ // summary: Adds content onto the buffer return buffer.concat(this.contents); }, isEmpty: function(){ return !lang.trim(this.contents); }, clone: function(){ return this; } }); dd._NodeList = lang.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); return this; }, concat: function(nodes){ this.contents = this.contents.concat(nodes); return this; }, 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; }, rtrim: function(){ while(1){ i = this.contents.length - 1; if(this.contents[i] instanceof dd._TextNode && this.contents[i].isEmpty()){ this.contents.pop(); }else{ break; } } return this; } }); dd._VarNode = lang.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); if(!str.safe){ str = dd._base.escape("" + str); } 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 = lang.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 terminators = {}, token; 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(typeof token == "string"){ nodelist.push(new dd._TextNode(token)); }else{ var type = token[0]; var text = token[1]; if(type == dd.TOKEN_VAR){ nodelist.push(new dd._VarNode(text)); }else if(type == dd.TOKEN_BLOCK){ 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, new dd.Token(type, text))); } } } } } if(stop_at.length){ throw new Error("Could not find closing tag(s): " + stop_at.toString()); } this.contents.length = 0; return nodelist; }, next_token: function(){ // summary: Returns the next token in the list. var token = this.contents[this.i++]; return new dd.Token(token[0], token[1]); }, delete_first_token: function(){ this.i++; }, skip_past: function(endtag){ while(this.i < this.contents.length){ var token = this.contents[this.i++]; if(token[0] == dd.TOKEN_BLOCK && token[1] == endtag){ return; } } throw new Error("Unclosed tag found when looking for " + endtag); }, create_variable_node: function(expr){ return new dd._VarNode(expr); }, create_text_node: function(expr){ return new dd._TextNode(expr || ""); }, 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(typeof entry[0] == "string"){ 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 = lang.getObject(entry[1]); if(fn && lang.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(lang.isArray(fn)){ key = fn[0]; fn = fn[1]; } if(typeof key == "string"){ 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.toLowerCase(), 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); } } var escapeamp = /&/g; var escapelt = //g; var escapeqt = /'/g; var escapedblqt = /"/g; dd._base.escape = function(value){ // summary: Escapes a string's HTML return dd.mark_safe(value.replace(escapeamp, '&').replace(escapelt, '<').replace(escapegt, '>').replace(escapedblqt, '"').replace(escapeqt, ''')); } dd._base.safe = function(value){ if(typeof value == "string"){ value = new String(value); } if(typeof value == "object"){ value.safe = true; } return value; } dd.mark_safe = dd._base.safe; 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": ["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"] }); dd.register.filters("dojox.dtl", { "_base": ["escape", "safe"] }); return dd; });