diff options
Diffstat (limited to 'js/dojo/dojox/dtl/_base.js')
| -rw-r--r-- | js/dojo/dojox/dtl/_base.js | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/js/dojo/dojox/dtl/_base.js b/js/dojo/dojox/dtl/_base.js new file mode 100644 index 0000000..3f11bb1 --- /dev/null +++ b/js/dojo/dojox/dtl/_base.js @@ -0,0 +1,766 @@ +//>>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 escapegt = />/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; +}); + |
