source: Dev/trunk/src/client/dojox/dtl/_base.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 21.1 KB
Line 
1define([
2        "dojo/_base/kernel",
3        "dojo/_base/lang",
4        "dojox/string/tokenize",
5        "dojo/_base/json",
6        "dojo/dom",
7        "dojo/_base/xhr",
8        "dojox/string/Builder",
9        "dojo/_base/Deferred"],
10        function(kernel, lang, Tokenize, json, dom, xhr, StringBuilder, deferred){
11
12        kernel.experimental("dojox.dtl");
13
14        var dd = lang.getObject("dojox.dtl", true);
15/*=====
16        dd = {
17                // TODO: summary
18        };
19=====*/
20
21        dd._base = {};
22
23        dd.TOKEN_BLOCK = -1;
24        dd.TOKEN_VAR = -2;
25        dd.TOKEN_COMMENT = -3;
26        dd.TOKEN_TEXT = 3;
27
28        dd._Context = lang.extend(function(dict){
29                // summary:
30                //              Pass one of these when rendering a template to tell the template what values to use.
31                if(dict){
32                        lang._mixin(this, dict);
33                        if(dict.get){
34                                // Preserve passed getter and restore prototype get
35                                this._getter = dict.get;
36                                delete this.get;
37                        }
38                }
39        },
40        {
41                push: function(){
42                        var last = this;
43                        var context = lang.delegate(this);
44                        context.pop = function(){ return last; }
45                        return context;
46                },
47                pop: function(){
48                        throw new Error("pop() called on empty Context");
49                },
50                get: function(key, otherwise){
51                        var n = this._normalize;
52
53                        if(this._getter){
54                                var got = this._getter(key);
55                                if(got !== undefined){
56                                        return n(got);
57                                }
58                        }
59
60                        if(this[key] !== undefined){
61                                return n(this[key]);
62                        }
63
64                        return otherwise;
65                },
66                _normalize: function(value){
67                        if(value instanceof Date){
68                                value.year = value.getFullYear();
69                                value.month = value.getMonth() + 1;
70                                value.day = value.getDate();
71                                value.date = value.year + "-" + ("0" + value.month).slice(-2) + "-" + ("0" + value.day).slice(-2);
72                                value.hour = value.getHours();
73                                value.minute = value.getMinutes();
74                                value.second = value.getSeconds();
75                                value.microsecond = value.getMilliseconds();
76                        }
77                        return value;
78                },
79                update: function(dict){
80                        var context = this.push();
81                        if(dict){
82                                lang._mixin(this, dict);
83                        }
84                        return context;
85                }
86        });
87
88        var smart_split_re = /("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+)/g;
89        var split_re = /\s+/g;
90        var split = function(/*String|RegExp?*/ splitter, /*Integer?*/ limit){
91                splitter = splitter || split_re;
92                if(!(splitter instanceof RegExp)){
93                        splitter = new RegExp(splitter, "g");
94                }
95                if(!splitter.global){
96                        throw new Error("You must use a globally flagged RegExp with split " + splitter);
97                }
98                splitter.exec(""); // Reset the global
99
100                var part, parts = [], lastIndex = 0, i = 0;
101                while((part = splitter.exec(this))){
102                        parts.push(this.slice(lastIndex, splitter.lastIndex - part[0].length));
103                        lastIndex = splitter.lastIndex;
104                        if(limit && (++i > limit - 1)){
105                                break;
106                        }
107                }
108                parts.push(this.slice(lastIndex));
109                return parts;
110        };
111
112        dd.Token = function(token_type, contents){
113                // tags:
114                //              private
115                this.token_type = token_type;
116                this.contents = new String(lang.trim(contents));
117                this.contents.split = split;
118                this.split = function(){
119                        return String.prototype.split.apply(this.contents, arguments);
120                }
121        };
122        dd.Token.prototype.split_contents = function(/*Integer?*/ limit){
123                var bit, bits = [], i = 0;
124                limit = limit || 999;
125                while(i++ < limit && (bit = smart_split_re.exec(this.contents))){
126                        bit = bit[0];
127                        if(bit.charAt(0) == '"' && bit.slice(-1) == '"'){
128                                bits.push('"' + bit.slice(1, -1).replace('\\"', '"').replace('\\\\', '\\') + '"');
129                        }else if(bit.charAt(0) == "'" && bit.slice(-1) == "'"){
130                                bits.push("'" + bit.slice(1, -1).replace("\\'", "'").replace('\\\\', '\\') + "'");
131                        }else{
132                                bits.push(bit);
133                        }
134                }
135                return bits;
136        };
137
138        var ddt = dd.text = {
139                _get: function(module, name, errorless){
140                        // summary:
141                        //              Used to find both tags and filters
142                        var params = dd.register.get(module, name.toLowerCase(), errorless);
143                        if(!params){
144                                if(!errorless){
145                                        throw new Error("No tag found for " + name);
146                                }
147                                return null;
148                        }
149
150                        var fn = params[1];
151                        var deps = params[2];
152
153                        var parts;
154                        if(fn.indexOf(":") != -1){
155                                parts = fn.split(":");
156                                fn = parts.pop();
157                        }
158
159// FIXME: THIS DESIGN DOES NOT WORK WITH ASYNC LOADERS!
160                        var mod = deps;
161                        if (/\./.test(deps)) {
162                                deps = deps.replace(/\./g, "/");
163                        }
164                        require([deps], function(){});
165
166                        var parent = lang.getObject(mod);
167
168                        return parent[fn || name] || parent[name + "_"] || parent[fn + "_"];
169                },
170                getTag: function(name, errorless){
171                        return ddt._get("tag", name, errorless);
172                },
173                getFilter: function(name, errorless){
174                        return ddt._get("filter", name, errorless);
175                },
176                getTemplate: function(file){
177                        return new dd.Template(ddt.getTemplateString(file));
178                },
179                getTemplateString: function(file){
180                        return xhr._getText(file.toString()) || "";
181                },
182                _resolveLazy: function(location, sync, json){
183                        if(sync){
184                                if(json){
185                                        return json.fromJson(xhr._getText(location)) || {};
186                                }else{
187                                        return dd.text.getTemplateString(location);
188                                }
189                        }else{
190                                return xhr.get({
191                                        handleAs: json ? "json" : "text",
192                                        url: location
193                                });
194                        }
195                },
196                _resolveTemplateArg: function(arg, sync){
197                        if(ddt._isTemplate(arg)){
198                                if(!sync){
199                                        var d = new deferred();
200                                        d.callback(arg);
201                                        return d;
202                                }
203                                return arg;
204                        }
205                        return ddt._resolveLazy(arg, sync);
206                },
207                _isTemplate: function(arg){
208                        return (arg === undefined) || (typeof arg == "string" && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
209                },
210                _resolveContextArg: function(arg, sync){
211                        if(arg.constructor == Object){
212                                if(!sync){
213                                        var d = new deferred;
214                                        d.callback(arg);
215                                        return d;
216                                }
217                                return arg;
218                        }
219                        return ddt._resolveLazy(arg, sync, true);
220                },
221                _re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
222                tokenize: function(str){
223                        return Tokenize(str, ddt._re, ddt._parseDelims);
224                },
225                _parseDelims: function(varr, load, tag){
226                        if(varr){
227                                return [dd.TOKEN_VAR, varr];
228                        }else if(load){
229                                var parts = lang.trim(tag).split(/\s+/g);
230                                for(var i = 0, part; part = parts[i]; i++){
231                                        if (/\./.test(part)){
232                                                part = part.replace(/\./g,"/");
233                                        }
234                                        require([part]);
235                                }
236                        }else{
237                                return [dd.TOKEN_BLOCK, tag];
238                        }
239                }
240        };
241
242        dd.Template = lang.extend(function(/*String|dojo._Url*/ template, /*Boolean*/ isString){
243                // summary:
244                //              The base class for text-based templates.
245                // template: String|dojo/_base/url
246                //              The string or location of the string to
247                //              use as a template
248                // isString: Boolean
249                //              Indicates whether the template is a string or a url.
250                var str = isString ? template : ddt._resolveTemplateArg(template, true) || "";
251                var tokens = ddt.tokenize(str);
252                var parser = new dd._Parser(tokens);
253                this.nodelist = parser.parse();
254        },
255        {
256                update: function(node, context){
257                        // summary:
258                        //              Updates this template according to the given context.
259                        // node: DOMNode|String|dojo/NodeList
260                        //              A node reference or set of nodes
261                        // context: dojo/base/url|String|Object
262                        //              The context object or location
263                        return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
264                                var content = this.render(new dd._Context(contextObject));
265                                if(node.forEach){
266                                        node.forEach(function(item){
267                                                item.innerHTML = content;
268                                        });
269                                }else{
270                                        dom.byId(node).innerHTML = content;
271                                }
272                                return this;
273                        });
274                },
275                render: function(context, buffer){
276                        // summary:
277                        //              Renders this template.
278                        // context: Object
279                        //              The runtime context.
280                        // buffer: StringBuilder?
281                        //              A string buffer.
282                        buffer = buffer || this.getBuffer();
283                        context = context || new dd._Context({});
284                        return this.nodelist.render(context, buffer) + "";
285                },
286                getBuffer: function(){
287                        return new StringBuilder();
288                }
289        });
290
291        var qfRe = /\{\{\s*(.+?)\s*\}\}/g;
292        dd.quickFilter = function(str){
293                if(!str){
294                        return new dd._NodeList();
295                }
296
297                if(str.indexOf("{%") == -1){
298                        return new dd._QuickNodeList(Tokenize(str, qfRe, function(token){
299                                return new dd._Filter(token);
300                        }));
301                }
302        };
303
304        dd._QuickNodeList = lang.extend(function(contents){
305                this.contents = contents;
306        },
307        {
308                render: function(context, buffer){
309                        for(var i = 0, l = this.contents.length; i < l; i++){
310                                if(this.contents[i].resolve){
311                                        buffer = buffer.concat(this.contents[i].resolve(context));
312                                }else{
313                                        buffer = buffer.concat(this.contents[i]);
314                                }
315                        }
316                        return buffer;
317                },
318                dummyRender: function(context){ return this.render(context, dd.Template.prototype.getBuffer()).toString(); },
319                clone: function(buffer){ return this; }
320        });
321
322        dd._Filter = lang.extend(function(token){
323                // summary:
324                //              Uses a string to find (and manipulate) a variable
325                if(!token) throw new Error("Filter must be called with variable name");
326                this.contents = token;
327
328                var cache = this._cache[token];
329                if(cache){
330                        this.key = cache[0];
331                        this.filters = cache[1];
332                }else{
333                        this.filters = [];
334                        Tokenize(token, this._re, this._tokenize, this);
335                        this._cache[token] = [this.key, this.filters];
336                }
337        },
338        {
339                _cache: {},
340                _re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
341                _values: {
342                        0: '"', // _("text")
343                        1: '"', // "text"
344                        2: "", // variable
345                        8: '"' // 'text'
346                },
347                _args: {
348                        4: '"', // :_("text")
349                        5: '"', // :"text"
350                        6: "", // :variable
351                        7: "'"// :'text'
352                },
353                _tokenize: function(){
354                        var pos, arg;
355
356                        for(var i = 0, has = []; i < arguments.length; i++){
357                                has[i] = (arguments[i] !== undefined && typeof arguments[i] == "string" && arguments[i]);
358                        }
359
360                        if(!this.key){
361                                for(pos in this._values){
362                                        if(has[pos]){
363                                                this.key = this._values[pos] + arguments[pos] + this._values[pos];
364                                                break;
365                                        }
366                                }
367                        }else{
368                                for(pos in this._args){
369                                        if(has[pos]){
370                                                var value = arguments[pos];
371                                                if(this._args[pos] == "'"){
372                                                        value = value.replace(/\\'/g, "'");
373                                                }else if(this._args[pos] == '"'){
374                                                        value = value.replace(/\\"/g, '"');
375                                                }
376                                                arg = [!this._args[pos], value];
377                                                break;
378                                        }
379                                }
380                                // Get a named filter
381                                var fn = ddt.getFilter(arguments[3]);
382                                if(!lang.isFunction(fn)) throw new Error(arguments[3] + " is not registered as a filter");
383                                this.filters.push([fn, arg]);
384                        }
385                },
386                getExpression: function(){
387                        return this.contents;
388                },
389                resolve: function(context){
390                        if(this.key === undefined){
391                                return "";
392                        }
393
394                        var str = this.resolvePath(this.key, context);
395
396                        for(var i = 0, filter; filter = this.filters[i]; i++){
397                                // Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
398                                // and [1][1] is either the variable name of the string content.
399                                if(filter[1]){
400                                        if(filter[1][0]){
401                                                str = filter[0](str, this.resolvePath(filter[1][1], context));
402                                        }else{
403                                                str = filter[0](str, filter[1][1]);
404                                        }
405                                }else{
406                                        str = filter[0](str);
407                                }
408                        }
409
410                        return str;
411                },
412                resolvePath: function(path, context){
413                        var current, parts;
414                        var first = path.charAt(0);
415                        var last = path.slice(-1);
416                        if(!isNaN(parseInt(first))){
417                                current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
418                        }else if(first == '"' && first == last){
419                                current = path.slice(1, -1);
420                        }else{
421                                if(path == "true"){ return true; }
422                                if(path == "false"){ return false; }
423                                if(path == "null" || path == "None"){ return null; }
424                                parts = path.split(".");
425                                current = context.get(parts[0]);
426
427                                if(lang.isFunction(current)){
428                                        var self = context.getThis && context.getThis();
429                                        if(current.alters_data){
430                                                current = "";
431                                        }else if(self){
432                                                current = current.call(self);
433                                        }else{
434                                                current = "";
435                                        }
436                                }
437
438                                for(var i = 1; i < parts.length; i++){
439                                        var part = parts[i];
440                                        if(current){
441                                                var base = current;
442                                                if(lang.isObject(current) && part == "items" && current[part] === undefined){
443                                                        var items = [];
444                                                        for(var key in current){
445                                                                items.push([key, current[key]]);
446                                                        }
447                                                        current = items;
448                                                        continue;
449                                                }
450
451                                                if(current.get && lang.isFunction(current.get) && current.get.safe){
452                                                        current = current.get(part);
453                                                }else if(current[part] === undefined){
454                                                        current = current[part];
455                                                        break;
456                                                }else{
457                                                        current = current[part];
458                                                }
459
460                                                if(lang.isFunction(current)){
461                                                        if(current.alters_data){
462                                                                current = "";
463                                                        }else{
464                                                                current = current.call(base);
465                                                        }
466                                                }else if(current instanceof Date){
467                                                        current = dd._Context.prototype._normalize(current);
468                                                }
469                                        }else{
470                                                return "";
471                                        }
472                                }
473                        }
474                        return current;
475                }
476        });
477
478        dd._TextNode = dd._Node = lang.extend(function(/*Object*/ obj){
479                // summary:
480                //              Basic catch-all node
481                this.contents = obj;
482        },
483        {
484                set: function(data){
485                        this.contents = data;
486                        return this;
487                },
488                render: function(context, buffer){
489                        // summary:
490                        //              Adds content onto the buffer
491                        return buffer.concat(this.contents);
492                },
493                isEmpty: function(){
494                        return !lang.trim(this.contents);
495                },
496                clone: function(){ return this; }
497        });
498
499        dd._NodeList = lang.extend(function(/*Node[]*/ nodes){
500                // summary:
501                //              Allows us to render a group of nodes
502                this.contents = nodes || [];
503                this.last = "";
504        },
505        {
506                push: function(node){
507                        // summary:
508                        //              Add a new node to the list
509                        this.contents.push(node);
510                        return this;
511                },
512                concat: function(nodes){
513                        this.contents = this.contents.concat(nodes);
514                        return this;
515                },
516                render: function(context, buffer){
517                        // summary:
518                        //              Adds all content onto the buffer
519                        for(var i = 0; i < this.contents.length; i++){
520                                buffer = this.contents[i].render(context, buffer);
521                                if(!buffer) throw new Error("Template must return buffer");
522                        }
523                        return buffer;
524                },
525                dummyRender: function(context){
526                        return this.render(context, dd.Template.prototype.getBuffer()).toString();
527                },
528                unrender: function(){ return arguments[1]; },
529                clone: function(){ return this; },
530                rtrim: function(){
531                        while(1){
532                                i = this.contents.length - 1;
533                                if(this.contents[i] instanceof dd._TextNode && this.contents[i].isEmpty()){
534                                        this.contents.pop();
535                                }else{
536                                        break;
537                                }
538                        }
539
540                        return this;
541                }
542        });
543
544        dd._VarNode = lang.extend(function(str){
545                // summary:
546                //              A node to be processed as a variable
547                this.contents = new dd._Filter(str);
548        },
549        {
550                render: function(context, buffer){
551                        var str = this.contents.resolve(context);
552                        if(!str.safe){
553                                str = dd._base.escape("" + str);
554                        }
555                        return buffer.concat(str);
556                }
557        });
558
559        dd._noOpNode = new function(){
560                // summary:
561                //              Adds a no-op node. Useful in custom tags
562                this.render = this.unrender = function(){ return arguments[1]; }
563                this.clone = function(){ return this; }
564        };
565
566        dd._Parser = lang.extend(function(tokens){
567                // summary:
568                //              Parser used during initialization and for tag groups.
569                this.contents = tokens;
570        },
571        {
572                i: 0,
573                parse: function(/*Array?*/ stop_at){
574                        // summary:
575                        //              Turns tokens into nodes
576                        // description:
577                        //              Steps into tags are they're found. Blocks use the parse object
578                        //              to find their closing tag (the stop_at array). stop_at is inclusive, it
579                        //              returns the node that matched.
580                        var terminators = {}, token;
581                        stop_at = stop_at || [];
582                        for(var i = 0; i < stop_at.length; i++){
583                                terminators[stop_at[i]] = true;
584                        }
585
586                        var nodelist = new dd._NodeList();
587                        while(this.i < this.contents.length){
588                                token = this.contents[this.i++];
589                                if(typeof token == "string"){
590                                        nodelist.push(new dd._TextNode(token));
591                                }else{
592                                        var type = token[0];
593                                        var text = token[1];
594                                        if(type == dd.TOKEN_VAR){
595                                                nodelist.push(new dd._VarNode(text));
596                                        }else if(type == dd.TOKEN_BLOCK){
597                                                if(terminators[text]){
598                                                        --this.i;
599                                                        return nodelist;
600                                                }
601                                                var cmd = text.split(/\s+/g);
602                                                if(cmd.length){
603                                                        cmd = cmd[0];
604                                                        var fn = ddt.getTag(cmd);
605                                                        if(fn){
606                                                                nodelist.push(fn(this, new dd.Token(type, text)));
607                                                        }
608                                                }
609                                        }
610                                }
611                        }
612
613                        if(stop_at.length){
614                                throw new Error("Could not find closing tag(s): " + stop_at.toString());
615                        }
616
617                        this.contents.length = 0;
618                        return nodelist;
619                },
620                next_token: function(){
621                        // summary:
622                        //              Returns the next token in the list.
623                        var token = this.contents[this.i++];
624                        return new dd.Token(token[0], token[1]);
625                },
626                delete_first_token: function(){
627                        this.i++;
628                },
629                skip_past: function(endtag){
630                        while(this.i < this.contents.length){
631                                var token = this.contents[this.i++];
632                                if(token[0] == dd.TOKEN_BLOCK && token[1] == endtag){
633                                        return;
634                                }
635                        }
636                        throw new Error("Unclosed tag found when looking for " + endtag);
637                },
638                create_variable_node: function(expr){
639                        return new dd._VarNode(expr);
640                },
641                create_text_node: function(expr){
642                        return new dd._TextNode(expr || "");
643                },
644                getTemplate: function(file){
645                        return new dd.Template(file);
646                }
647        });
648
649        dd.register = {
650                // summary:
651                //              A register for filters and tags.
652               
653                _registry: {
654                        attributes: [],
655                        tags: [],
656                        filters: []
657                },
658                get: function(/*String*/ module, /*String*/ name){
659                        // tags:
660                        //              private
661                        var registry = dd.register._registry[module + "s"];
662                        for(var i = 0, entry; entry = registry[i]; i++){
663                                if(typeof entry[0] == "string"){
664                                        if(entry[0] == name){
665                                                return entry;
666                                        }
667                                }else if(name.match(entry[0])){
668                                        return entry;
669                                }
670                        }
671                },
672                getAttributeTags: function(){
673                        // tags:
674                        //              private
675                        var tags = [];
676                        var registry = dd.register._registry.attributes;
677                        for(var i = 0, entry; entry = registry[i]; i++){
678                                if(entry.length == 3){
679                                        tags.push(entry);
680                                }else{
681                                        var fn = lang.getObject(entry[1]);
682                                        if(fn && lang.isFunction(fn)){
683                                                entry.push(fn);
684                                                tags.push(entry);
685                                        }
686                                }
687                        }
688                        return tags;
689                },
690                _any: function(type, base, locations){
691                        for(var path in locations){
692                                for(var i = 0, fn; fn = locations[path][i]; i++){
693                                        var key = fn;
694                                        if(lang.isArray(fn)){
695                                                key = fn[0];
696                                                fn = fn[1];
697                                        }
698                                        if(typeof key == "string"){
699                                                if(key.substr(0, 5) == "attr:"){
700                                                        var attr = fn;
701                                                        if(attr.substr(0, 5) == "attr:"){
702                                                                attr = attr.slice(5);
703                                                        }
704                                                        dd.register._registry.attributes.push([attr.toLowerCase(), base + "." + path + "." + attr]);
705                                                }
706                                                key = key.toLowerCase()
707                                        }
708                                        dd.register._registry[type].push([
709                                                key,
710                                                fn,
711                                                base + "." + path
712                                        ]);
713                                }
714                        }
715                },
716                tags: function(/*String*/ base, /*Object*/ locations){
717                        // summary:
718                        //              Register the specified tag libraries.
719                        // description:
720                        //              The locations parameter defines the contents of each library as a hash whose keys are the library names and values
721                        //              an array of the tags exported by the library. For example, the tags exported by the logic library would be:
722                        //      |       { logic: ["if", "for", "ifequal", "ifnotequal"] }
723                        // base:
724                        //              The base path of the libraries.
725                        // locations:
726                        //              An object defining the tags for each library as a hash whose keys are the library names and values
727                        //              an array of the tags or filters exported by the library.
728                        dd.register._any("tags", base, locations);
729                },
730                filters: function(/*String*/ base, /*Object*/ locations){
731                        // summary:
732                        //              Register the specified filter libraries.
733                        // description:
734                        //              The locations parameter defines the contents of each library as a hash whose keys are the library names and values
735                        //              an array of the filters exported by the library. For example, the filters exported by the date library would be:
736                        //      |       { "dates": ["date", "time", "timesince", "timeuntil"] }
737                        // base:
738                        //              The base path of the libraries.
739                        // locations:
740                        //              An object defining the filters for each library as a hash whose keys are the library names and values
741                        //              an array of the filters exported by the library.
742                        dd.register._any("filters", base, locations);
743                }
744        }
745
746        var escapeamp = /&/g;
747        var escapelt = /</g;
748        var escapegt = />/g;
749        var escapeqt = /'/g;
750        var escapedblqt = /"/g;
751        dd._base.escape = function(value){
752                // summary:
753                //              Escapes a string's HTML
754                return dd.mark_safe(value.replace(escapeamp, '&amp;').replace(escapelt, '&lt;').replace(escapegt, '&gt;').replace(escapedblqt, '&quot;').replace(escapeqt, '&#39;'));
755        };
756
757        dd._base.safe = function(value){
758                if(typeof value == "string"){
759                        value = new String(value);
760                }
761                if(typeof value == "object"){
762                        value.safe = true;
763                }
764                return value;
765        };
766        dd.mark_safe = dd._base.safe;
767
768        dd.register.tags("dojox.dtl.tag", {
769                "date": ["now"],
770                "logic": ["if", "for", "ifequal", "ifnotequal"],
771                "loader": ["extends", "block", "include", "load", "ssi"],
772                "misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
773                "loop": ["cycle", "ifchanged", "regroup"]
774        });
775        dd.register.filters("dojox.dtl.filter", {
776                "dates": ["date", "time", "timesince", "timeuntil"],
777                "htmlstrings": ["linebreaks", "linebreaksbr", "removetags", "striptags"],
778                "integers": ["add", "get_digit"],
779                "lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
780                "logic": ["default", "default_if_none", "divisibleby", "yesno"],
781                "misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
782                "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"]
783        });
784        dd.register.filters("dojox.dtl", {
785                "_base": ["escape", "safe"]
786        });
787
788        return dd;
789});
790
Note: See TracBrowser for help on using the repository browser.