source: Dev/branches/rest-dojo-ui/client/dojox/dtl/_base.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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