1 if(!dojo._hasResource["dojox.dtl._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.dtl._base"] = true;
3 dojo.provide("dojox.dtl._base");
5 dojo.require("dojox.string.Builder");
6 dojo.require("dojox.string.tokenize");
11 dd._Context = dojo.extend(function(dict){
12 // summary: Pass one of these when rendering a template to tell the template what values to use.
13 dojo.mixin(this, dict || {});
19 var keys = this.getKeys();
20 for(var i = 0, key; key = keys[i]; i++){
21 dict[key] = this[key];
24 this._dicts.unshift(dict);
27 if(!this._dicts.length){
28 throw new Error("pop() called on empty Context");
30 var dict = this._dicts.shift();
31 dojo.mixin(this, dict);
36 if(this.hasOwnProperty(key) && key != "_dicts" && key != "_this"){
42 get: function(key, otherwise){
43 if(typeof this[key] != "undefined"){
44 return this._normalize(this[key]);
47 for(var i = 0, dict; dict = this._dicts[i]; i++){
48 if(typeof dict[key] != "undefined"){
49 return this._normalize(dict[key]);
55 _normalize: function(value){
56 if(value instanceof Date){
57 value.year = value.getFullYear();
58 value.month = value.getMonth() + 1;
59 value.day = value.getDate();
60 value.date = value.year + "-" + ("0" + value.month).slice(-2) + "-" + ("0" + value.day).slice(-2);
61 value.hour = value.getHours();
62 value.minute = value.getMinutes();
63 value.second = value.getSeconds();
64 value.microsecond = value.getMilliseconds();
68 update: function(dict){
71 dojo.mixin(this, dict);
77 types: {tag: -1, varr: -2, text: 3},
78 pySplit: function(str){
79 // summary: Split a string according to Python's split function
81 return (!str.length) ? [] : str.split(/\s+/g);
83 _get: function(module, name, errorless){
84 // summary: Used to find both tags and filters
85 var params = dd.register.get(module, name.toLowerCase(), errorless);
88 throw new Error("No tag found for " + name);
94 var require = params[2];
97 if(fn.indexOf(":") != -1){
98 parts = fn.split(":");
102 dojo["require"](require);
104 var parent = dojo.getObject(require);
106 return parent[fn || name] || parent[name + "_"];
108 getTag: function(name, errorless){
109 return ddt._get("tag", name, errorless);
111 getFilter: function(name, errorless){
112 return ddt._get("filter", name, errorless);
114 getTemplate: function(file){
115 return new dd.Template(dd.getTemplateString(file));
117 getTemplateString: function(file){
118 return dojo._getText(file.toString()) || "";
120 _resolveLazy: function(location, sync, json){
123 return dojo.fromJson(dojo._getText(location)) || {};
125 return dd.text.getTemplateString(location);
129 handleAs: (json) ? "json" : "text",
134 _resolveTemplateArg: function(arg, sync){
135 if(ddt._isTemplate(arg)){
137 var d = new dojo.Deferred();
143 return ddt._resolveLazy(arg, sync);
145 _isTemplate: function(arg){
146 return (typeof arg == "undefined") || (dojo.isString(arg) && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
148 _resolveContextArg: function(arg, sync){
149 if(arg.constructor == Object){
151 var d = new dojo.Deferred;
157 return ddt._resolveLazy(arg, sync, true);
159 _re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
160 tokenize: function(str){
161 return dojox.string.tokenize(str, ddt._re, ddt._parseDelims);
163 _parseDelims: function(varr, load, tag){
164 var types = ddt.types;
166 return [types.varr, varr];
168 var parts = dd.text.pySplit(tag);
169 for(var i = 0, part; part = parts[i]; i++){
170 dojo["require"](part);
173 return [types.tag, tag];
178 dd.Template = dojo.extend(function(/*String|dojo._Url*/ template){
180 // The string or location of the string to
182 var str = ddt._resolveTemplateArg(template, true) || "";
183 var tokens = ddt.tokenize(str);
184 var parser = new dd._Parser(tokens);
185 this.nodelist = parser.parse();
188 update: function(node, context){
189 // node: DOMNode|String|dojo.NodeList
190 // A node reference or set of nodes
191 // context: dojo._Url|String|Object
192 // The context object or location
193 return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
194 var content = this.render(new dd._Context(contextObject));
196 node.forEach(function(item){
197 item.innerHTML = content;
200 dojo.byId(node).innerHTML = content;
205 render: function(context, /*concatenatable?*/ buffer){
206 buffer = buffer || this.getBuffer();
207 context = context || new dd._Context({});
208 return this.nodelist.render(context, buffer) + "";
210 getBuffer: function(){
211 dojo.require("dojox.string.Builder");
212 return new dojox.string.Builder();
216 dd._Filter = dojo.extend(function(token){
217 // summary: Uses a string to find (and manipulate) a variable
218 if(!token) throw new Error("Filter must be called with variable name");
219 this.contents = token;
221 var cache = this._cache[token];
224 this.filters = cache[1];
227 dojox.string.tokenize(token, this._re, this._tokenize, this);
228 this._cache[token] = [this.key, this.filters];
233 _re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
241 4: '"', // :_("text")
246 _tokenize: function(){
249 for(var i = 0, has = []; i < arguments.length; i++){
250 has[i] = (typeof arguments[i] != "undefined" && dojo.isString(arguments[i]) && arguments[i]);
254 for(pos in this._values){
256 this.key = this._values[pos] + arguments[pos] + this._values[pos];
261 for(pos in this._args){
263 var value = arguments[pos];
264 if(this._args[pos] == "'"){
265 value = value.replace(/\\'/g, "'");
266 }else if(this._args[pos] == '"'){
267 value = value.replace(/\\"/g, '"');
269 arg = [!this._args[pos], value];
273 // Get a named filter
274 var fn = ddt.getFilter(arguments[3]);
275 if(!dojo.isFunction(fn)) throw new Error(arguments[3] + " is not registered as a filter");
276 this.filters.push([fn, arg]);
279 getExpression: function(){
280 return this.contents;
282 resolve: function(context){
283 var str = this.resolvePath(this.key, context);
284 for(var i = 0, filter; filter = this.filters[i]; i++){
285 // Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
286 // and [1][1] is either the variable name of the string content.
289 str = filter[0](str, this.resolvePath(filter[1][1], context));
291 str = filter[0](str, filter[1][1]);
294 str = filter[0](str);
299 resolvePath: function(path, context){
301 var first = path.charAt(0);
302 var last = path.slice(-1);
303 if(!isNaN(parseInt(first))){
304 current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
305 }else if(first == '"' && first == last){
306 current = path.slice(1, -1);
308 if(path == "true"){ return true; }
309 if(path == "false"){ return false; }
310 if(path == "null" || path == "None"){ return null; }
311 parts = path.split(".");
312 current = context.get(parts[0]);
313 for(var i = 1; i < parts.length; i++){
316 if(dojo.isObject(current) && part == "items" && typeof current[part] == "undefined"){
318 for(var key in current){
319 items.push([key, current[key]]);
325 if(current.get && dojo.isFunction(current.get)){
326 current = current.get(part);
327 }else if(typeof current[part] == "undefined"){
328 current = current[part];
331 current = current[part];
334 if(dojo.isFunction(current)){
335 if(current.alters_data){
350 dd._TextNode = dd._Node = dojo.extend(function(/*Object*/ obj){
351 // summary: Basic catch-all node
356 this.contents = data;
358 render: function(context, buffer){
359 // summary: Adds content onto the buffer
360 return buffer.concat(this.contents);
364 dd._NodeList = dojo.extend(function(/*Node[]*/ nodes){
365 // summary: Allows us to render a group of nodes
366 this.contents = nodes || [];
370 push: function(node){
371 // summary: Add a new node to the list
372 this.contents.push(node);
374 render: function(context, buffer){
375 // summary: Adds all content onto the buffer
376 for(var i = 0; i < this.contents.length; i++){
377 buffer = this.contents[i].render(context, buffer);
378 if(!buffer) throw new Error("Template must return buffer");
382 dummyRender: function(context){
383 return this.render(context, dd.Template.prototype.getBuffer()).toString();
385 unrender: function(){ return arguments[1]; },
386 clone: function(){ return this; }
389 dd._VarNode = dojo.extend(function(str){
390 // summary: A node to be processed as a variable
391 this.contents = new dd._Filter(str);
394 render: function(context, buffer){
395 var str = this.contents.resolve(context);
396 return buffer.concat(str);
400 dd._noOpNode = new function(){
401 // summary: Adds a no-op node. Useful in custom tags
402 this.render = this.unrender = function(){ return arguments[1]; }
403 this.clone = function(){ return this; }
406 dd._Parser = dojo.extend(function(tokens){
407 // summary: Parser used during initialization and for tag groups.
408 this.contents = tokens;
412 parse: function(/*Array?*/ stop_at){
413 // summary: Turns tokens into nodes
414 // description: Steps into tags are they're found. Blocks use the parse object
415 // to find their closing tag (the stop_at array). stop_at is inclusive, it
416 // returns the node that matched.
417 var types = ddt.types;
418 var terminators = {};
419 stop_at = stop_at || [];
420 for(var i = 0; i < stop_at.length; i++){
421 terminators[stop_at[i]] = true;
424 var nodelist = new dd._NodeList();
425 while(this.i < this.contents.length){
426 token = this.contents[this.i++];
427 if(dojo.isString(token)){
428 nodelist.push(new dd._TextNode(token));
432 if(type == types.varr){
433 nodelist.push(new dd._VarNode(text));
434 }else if(type == types.tag){
435 if(terminators[text]){
439 var cmd = text.split(/\s+/g);
442 var fn = ddt.getTag(cmd);
444 nodelist.push(fn(this, text));
452 throw new Error("Could not find closing tag(s): " + stop_at.toString());
455 this.contents.length = 0;
459 // summary: Returns the next token in the list.
460 var token = this.contents[this.i++];
461 return {type: token[0], text: token[1]};
463 skipPast: function(endtag){
464 var types = ddt.types;
465 while(this.i < this.contents.length){
466 var token = this.contents[this.i++];
467 if(token[0] == types.tag && token[1] == endtag){
471 throw new Error("Unclosed tag found when looking for " + endtag);
473 getVarNodeConstructor: function(){
476 getTextNodeConstructor: function(){
479 getTemplate: function(file){
480 return new dd.Template(file);
490 get: function(/*String*/ module, /*String*/ name){
491 var registry = dd.register._registry[module + "s"];
492 for(var i = 0, entry; entry = registry[i]; i++){
493 if(dojo.isString(entry[0])){
494 if(entry[0] == name){
497 }else if(name.match(entry[0])){
502 getAttributeTags: function(){
504 var registry = dd.register._registry.attributes;
505 for(var i = 0, entry; entry = registry[i]; i++){
506 if(entry.length == 3){
509 var fn = dojo.getObject(entry[1]);
510 if(fn && dojo.isFunction(fn)){
518 _any: function(type, base, locations){
519 for(var path in locations){
520 for(var i = 0, fn; fn = locations[path][i]; i++){
522 if(dojo.isArray(fn)){
526 if(dojo.isString(key)){
527 if(key.substr(0, 5) == "attr:"){
529 if(attr.substr(0, 5) == "attr:"){
530 attr = attr.slice(5);
532 dd.register._registry.attributes.push([attr, base + "." + path + "." + attr]);
534 key = key.toLowerCase();
536 dd.register._registry[type].push([
544 tags: function(/*String*/ base, /*Object*/ locations){
545 dd.register._any("tags", base, locations);
547 filters: function(/*String*/ base, /*Object*/ locations){
548 dd.register._any("filters", base, locations);
552 dd.register.tags("dojox.dtl.tag", {
554 "logic": ["if", "for", "ifequal", "ifnotequal"],
555 "loader": ["extends", "block", "include", "load", "ssi"],
556 "misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
557 "loop": ["cycle", "ifchanged", "regroup"]
559 dd.register.filters("dojox.dtl.filter", {
560 "dates": ["date", "time", "timesince", "timeuntil"],
561 "htmlstrings": ["escape", "linebreaks", "linebreaksbr", "removetags", "striptags"],
562 "integers": ["add", "get_digit"],
563 "lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
564 "logic": ["default", "default_if_none", "divisibleby", "yesno"],
565 "misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
566 "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"]