Project

General

Profile

Statistics
| Revision:

root / trunk / web / dojo / dojox / dtl.js.uncompressed.js

History | View | Annotate | Download (78.9 KB)

1 9 andrej.cim
/*
2
        Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3
        Available via Academic Free License >= 2.1 OR the modified BSD license.
4
        see: http://dojotoolkit.org/license for details
5
*/
6
7
/*
8
        This is an optimized version of Dojo, built for deployment and not for
9
        development. To get sources and documentation, please visit:
10

11
                http://dojotoolkit.org
12
*/
13
14
if(!dojo._hasResource["dojox.string.Builder"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15
dojo._hasResource["dojox.string.Builder"] = true;
16
dojo.provide("dojox.string.Builder");
17
18
dojox.string.Builder = function(/*String?*/str){
19
        //        summary:
20
        //                A fast buffer for creating large strings.
21
        //
22
        //        length: Number
23
        //                The current length of the internal string.
24
25
        //        N.B. the public nature of the internal buffer is no longer
26
        //        needed because the IE-specific fork is no longer needed--TRT.
27
        var b = "";
28
        this.length = 0;
29
30
        this.append = function(/* String... */s){
31
                // summary: Append all arguments to the end of the buffer
32
                if(arguments.length>1){
33
                        /*
34
                                This is a loop unroll was designed specifically for Firefox;
35
                                it would seem that static index access on an Arguments
36
                                object is a LOT faster than doing dynamic index access.
37
                                Therefore, we create a buffer string and take advantage
38
                                of JS's switch fallthrough.  The peformance of this method
39
                                comes very close to straight up string concatenation (+=).
40

41
                                If the arguments object length is greater than 9, we fall
42
                                back to standard dynamic access.
43

44
                                This optimization seems to have no real effect on either
45
                                Safari or Opera, so we just use it for all.
46

47
                                It turns out also that this loop unroll can increase performance
48
                                significantly with Internet Explorer, particularly when
49
                                as many arguments are provided as possible.
50

51
                                Loop unroll per suggestion from Kris Zyp, implemented by
52
                                Tom Trenka.
53

54
                                Note: added empty string to force a string cast if needed.
55
                         */
56
                        var tmp="", l=arguments.length;
57
                        switch(l){
58
                                case 9: tmp=""+arguments[8]+tmp;
59
                                case 8: tmp=""+arguments[7]+tmp;
60
                                case 7: tmp=""+arguments[6]+tmp;
61
                                case 6: tmp=""+arguments[5]+tmp;
62
                                case 5: tmp=""+arguments[4]+tmp;
63
                                case 4: tmp=""+arguments[3]+tmp;
64
                                case 3: tmp=""+arguments[2]+tmp;
65
                                case 2: {
66
                                        b+=""+arguments[0]+arguments[1]+tmp;
67
                                        break;
68
                                }
69
                                default: {
70
                                        var i=0;
71
                                        while(i<arguments.length){
72
                                                tmp += arguments[i++];
73
                                        }
74
                                        b += tmp;
75
                                }
76
                        }
77
                } else {
78
                        b += s;
79
                }
80
                this.length = b.length;
81
                return this;        //        dojox.string.Builder
82
        };
83
84
        this.concat = function(/*String...*/s){
85
                //        summary:
86
                //                Alias for append.
87
                return this.append.apply(this, arguments);        //        dojox.string.Builder
88
        };
89
90
        this.appendArray = function(/*Array*/strings) {
91
                //        summary:
92
                //                Append an array of items to the internal buffer.
93
94
                //        Changed from String.prototype.concat.apply because of IE.
95
                return this.append.apply(this, strings);        //        dojox.string.Builder
96
        };
97
98
        this.clear = function(){
99
                //        summary:
100
                //                Remove all characters from the buffer.
101
                b = "";
102
                this.length = 0;
103
                return this;        //        dojox.string.Builder
104
        };
105
106
        this.replace = function(/* String */oldStr, /* String */ newStr){
107
                //         summary:
108
                //                Replace instances of one string with another in the buffer.
109
                b = b.replace(oldStr,newStr);
110
                this.length = b.length;
111
                return this;        //        dojox.string.Builder
112
        };
113
114
        this.remove = function(/* Number */start, /* Number? */len){
115
                //        summary:
116
                //                Remove len characters starting at index start.  If len
117
                //                is not provided, the end of the string is assumed.
118
                if(len===undefined){ len = b.length; }
119
                if(len == 0){ return this; }
120
                b = b.substr(0, start) + b.substr(start+len);
121
                this.length = b.length;
122
                return this;        //        dojox.string.Builder
123
        };
124
125
        this.insert = function(/* Number */index, /* String */str){
126
                //        summary:
127
                //                Insert string str starting at index.
128
                if(index == 0){
129
                        b = str + b;
130
                }else{
131
                        b = b.slice(0, index) + str + b.slice(index);
132
                }
133
                this.length = b.length;
134
                return this;        //        dojox.string.Builder
135
        };
136
137
        this.toString = function(){
138
                //        summary:
139
                //                Return the string representation of the internal buffer.
140
                return b;        //        String
141
        };
142
143
        //        initialize the buffer.
144
        if(str){ this.append(str); }
145
};
146
147
}
148
149
if(!dojo._hasResource["dojox.string.tokenize"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
150
dojo._hasResource["dojox.string.tokenize"] = true;
151
dojo.provide("dojox.string.tokenize");
152
153
dojox.string.tokenize = function(/*String*/ str, /*RegExp*/ re, /*Function?*/ parseDelim, /*Object?*/ instance){
154
        // summary:
155
        //                Split a string by a regular expression with the ability to capture the delimeters
156
        // parseDelim:
157
        //                Each group (excluding the 0 group) is passed as a parameter. If the function returns
158
        //                a value, it's added to the list of tokens.
159
        // instance:
160
        //                Used as the "this" instance when calling parseDelim
161
        var tokens = [];
162
        var match, content, lastIndex = 0;
163
        while(match = re.exec(str)){
164
                content = str.slice(lastIndex, re.lastIndex - match[0].length);
165
                if(content.length){
166
                        tokens.push(content);
167
                }
168
                if(parseDelim){
169
                        if(dojo.isOpera){
170
                                var copy = match.slice(0);
171
                                while(copy.length < match.length){
172
                                        copy.push(null);
173
                                }
174
                                match = copy;
175
                        }
176
                        var parsed = parseDelim.apply(instance, match.slice(1).concat(tokens.length));
177
                        if(typeof parsed != "undefined"){
178
                                tokens.push(parsed);
179
                        }
180
                }
181
                lastIndex = re.lastIndex;
182
        }
183
        content = str.slice(lastIndex);
184
        if(content.length){
185
                tokens.push(content);
186
        }
187
        return tokens;
188
}
189
190
}
191
192
if(!dojo._hasResource["dojox.dtl._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
193
dojo._hasResource["dojox.dtl._base"] = true;
194
dojo.provide("dojox.dtl._base");
195
196
197
198
199
dojo.experimental("dojox.dtl");
200
201
(function(){
202
        var dd = dojox.dtl;
203
204
        dd.TOKEN_BLOCK = -1;
205
        dd.TOKEN_VAR = -2;
206
        dd.TOKEN_COMMENT = -3;
207
        dd.TOKEN_TEXT = 3;
208
209
        dd._Context = dojo.extend(function(dict){
210
                // summary: Pass one of these when rendering a template to tell the template what values to use.
211
                if(dict){
212
                        dojo._mixin(this, dict);
213
                        if(dict.get){
214
                                // Preserve passed getter and restore prototype get
215
                                this._getter = dict.get;
216
                                delete this.get;
217
                        }
218
                }
219
        },
220
        {
221
                push: function(){
222
                        var last = this;
223
                        var context = dojo.delegate(this);
224
                        context.pop = function(){ return last; }
225
                        return context;
226
                },
227
                pop: function(){
228
                        throw new Error("pop() called on empty Context");
229
                },
230
                get: function(key, otherwise){
231
                        var n = this._normalize;
232
233
                        if(this._getter){
234
                                var got = this._getter(key);
235
                                if(typeof got != "undefined"){
236
                                        return n(got);
237
                                }
238
                        }
239
240
                        if(typeof this[key] != "undefined"){
241
                                return n(this[key]);
242
                        }
243
244
                        return otherwise;
245
                },
246
                _normalize: function(value){
247
                        if(value instanceof Date){
248
                                value.year = value.getFullYear();
249
                                value.month = value.getMonth() + 1;
250
                                value.day = value.getDate();
251
                                value.date = value.year + "-" + ("0" + value.month).slice(-2) + "-" + ("0" + value.day).slice(-2);
252
                                value.hour = value.getHours();
253
                                value.minute = value.getMinutes();
254
                                value.second = value.getSeconds();
255
                                value.microsecond = value.getMilliseconds();
256
                        }
257
                        return value;
258
                },
259
                update: function(dict){
260
                        var context = this.push();
261
                        if(dict){
262
                                dojo._mixin(this, dict);
263
                        }
264
                        return context;
265
                }
266
        });
267
268
        var smart_split_re = /("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+)/g;
269
        var split_re = /\s+/g;
270
        var split = function(/*String|RegExp?*/ splitter, /*Integer?*/ limit){
271
                splitter = splitter || split_re;
272
                if(!(splitter instanceof RegExp)){
273
                        splitter = new RegExp(splitter, "g");
274
                }
275
                if(!splitter.global){
276
                        throw new Error("You must use a globally flagged RegExp with split " + splitter);
277
                }
278
                splitter.exec(""); // Reset the global
279
280
                var part, parts = [], lastIndex = 0, i = 0;
281
                while(part = splitter.exec(this)){
282
                        parts.push(this.slice(lastIndex, splitter.lastIndex - part[0].length));
283
                        lastIndex = splitter.lastIndex;
284
                        if(limit && (++i > limit - 1)){
285
                                break;
286
                        }
287
                }
288
                parts.push(this.slice(lastIndex));
289
                return parts;
290
        }
291
292
        dd.Token = function(token_type, contents){
293
                this.token_type = token_type;
294
                this.contents = new String(dojo.trim(contents));
295
                this.contents.split = split;
296
                this.split = function(){
297
                        return String.prototype.split.apply(this.contents, arguments);
298
                }
299
        }
300
        dd.Token.prototype.split_contents = function(/*Integer?*/ limit){
301
                var bit, bits = [], i = 0;
302
                limit = limit || 999;
303
                while(i++ < limit && (bit = smart_split_re.exec(this.contents))){
304
                        bit = bit[0];
305
                        if(bit.charAt(0) == '"' && bit.slice(-1) == '"'){
306
                                bits.push('"' + bit.slice(1, -1).replace('\\"', '"').replace('\\\\', '\\') + '"');
307
                        }else if(bit.charAt(0) == "'" && bit.slice(-1) == "'"){
308
                                bits.push("'" + bit.slice(1, -1).replace("\\'", "'").replace('\\\\', '\\') + "'");
309
                        }else{
310
                                bits.push(bit);
311
                        }
312
                }
313
                return bits;
314
        }
315
316
        var ddt = dd.text = {
317
                _get: function(module, name, errorless){
318
                        // summary: Used to find both tags and filters
319
                        var params = dd.register.get(module, name.toLowerCase(), errorless);
320
                        if(!params){
321
                                if(!errorless){
322
                                        throw new Error("No tag found for " + name);
323
                                }
324
                                return null;
325
                        }
326
327
                        var fn = params[1];
328
                        var require = params[2];
329
330
                        var parts;
331
                        if(fn.indexOf(":") != -1){
332
                                parts = fn.split(":");
333
                                fn = parts.pop();
334
                        }
335
336
                        dojo["require"](require);
337
338
                        var parent = dojo.getObject(require);
339
340
                        return parent[fn || name] || parent[name + "_"] || parent[fn + "_"];
341
                },
342
                getTag: function(name, errorless){
343
                        return ddt._get("tag", name, errorless);
344
                },
345
                getFilter: function(name, errorless){
346
                        return ddt._get("filter", name, errorless);
347
                },
348
                getTemplate: function(file){
349
                        return new dd.Template(ddt.getTemplateString(file));
350
                },
351
                getTemplateString: function(file){
352
                        return dojo._getText(file.toString()) || "";
353
                },
354
                _resolveLazy: function(location, sync, json){
355
                        if(sync){
356
                                if(json){
357
                                        return dojo.fromJson(dojo._getText(location)) || {};
358
                                }else{
359
                                        return dd.text.getTemplateString(location);
360
                                }
361
                        }else{
362
                                return dojo.xhrGet({
363
                                        handleAs: (json) ? "json" : "text",
364
                                        url: location
365
                                });
366
                        }
367
                },
368
                _resolveTemplateArg: function(arg, sync){
369
                        if(ddt._isTemplate(arg)){
370
                                if(!sync){
371
                                        var d = new dojo.Deferred();
372
                                        d.callback(arg);
373
                                        return d;
374
                                }
375
                                return arg;
376
                        }
377
                        return ddt._resolveLazy(arg, sync);
378
                },
379
                _isTemplate: function(arg){
380
                        return (typeof arg == "undefined") || (typeof arg == "string" && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
381
                },
382
                _resolveContextArg: function(arg, sync){
383
                        if(arg.constructor == Object){
384
                                if(!sync){
385
                                        var d = new dojo.Deferred;
386
                                        d.callback(arg);
387
                                        return d;
388
                                }
389
                                return arg;
390
                        }
391
                        return ddt._resolveLazy(arg, sync, true);
392
                },
393
                _re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
394
                tokenize: function(str){
395
                        return dojox.string.tokenize(str, ddt._re, ddt._parseDelims);
396
                },
397
                _parseDelims: function(varr, load, tag){
398
                        if(varr){
399
                                return [dd.TOKEN_VAR, varr];
400
                        }else if(load){
401
                                var parts = dojo.trim(tag).split(/\s+/g);
402
                                for(var i = 0, part; part = parts[i]; i++){
403
                                        dojo["require"](part);
404
                                }
405
                        }else{
406
                                return [dd.TOKEN_BLOCK, tag];
407
                        }
408
                }
409
        }
410
411
        dd.Template = dojo.extend(function(/*String|dojo._Url*/ template, /*Boolean*/ isString){
412
                // template:
413
                //                The string or location of the string to
414
                //                use as a template
415
                var str = isString ? template : ddt._resolveTemplateArg(template, true) || "";
416
                var tokens = ddt.tokenize(str);
417
                var parser = new dd._Parser(tokens);
418
                this.nodelist = parser.parse();
419
        },
420
        {
421
                update: function(node, context){
422
                        // node: DOMNode|String|dojo.NodeList
423
                        //                A node reference or set of nodes
424
                        // context: dojo._Url|String|Object
425
                        //                The context object or location
426
                        return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
427
                                var content = this.render(new dd._Context(contextObject));
428
                                if(node.forEach){
429
                                        node.forEach(function(item){
430
                                                item.innerHTML = content;
431
                                        });
432
                                }else{
433
                                        dojo.byId(node).innerHTML = content;
434
                                }
435
                                return this;
436
                        });
437
                },
438
                render: function(context, /*concatenatable?*/ buffer){
439
                        buffer = buffer || this.getBuffer();
440
                        context = context || new dd._Context({});
441
                        return this.nodelist.render(context, buffer) + "";
442
                },
443
                getBuffer: function(){
444
445
                        return new dojox.string.Builder();
446
                }
447
        });
448
449
        var qfRe = /\{\{\s*(.+?)\s*\}\}/g;
450
        dd.quickFilter = function(str){
451
                if(!str){
452
                        return new dd._NodeList();
453
                }
454
455
                if(str.indexOf("{%") == -1){
456
                        return new dd._QuickNodeList(dojox.string.tokenize(str, qfRe, function(token){
457
                                return new dd._Filter(token);
458
                        }));
459
                }
460
        }
461
462
        dd._QuickNodeList = dojo.extend(function(contents){
463
                this.contents = contents;
464
        },
465
        {
466
                render: function(context, buffer){
467
                        for(var i=0, l=this.contents.length; i<l; i++){
468
                                if(this.contents[i].resolve){
469
                                        buffer = buffer.concat(this.contents[i].resolve(context));
470
                                }else{
471
                                        buffer = buffer.concat(this.contents[i]);
472
                                }
473
                        }
474
                        return buffer;
475
                },
476
                dummyRender: function(context){ return this.render(context, dd.Template.prototype.getBuffer()).toString(); },
477
                clone: function(buffer){ return this; }
478
        });
479
480
        dd._Filter = dojo.extend(function(token){
481
                // summary: Uses a string to find (and manipulate) a variable
482
                if(!token) throw new Error("Filter must be called with variable name");
483
                this.contents = token;
484
485
                var cache = this._cache[token];
486
                if(cache){
487
                        this.key = cache[0];
488
                        this.filters = cache[1];
489
                }else{
490
                        this.filters = [];
491
                        dojox.string.tokenize(token, this._re, this._tokenize, this);
492
                        this._cache[token] = [this.key, this.filters];
493
                }
494
        },
495
        {
496
                _cache: {},
497
                _re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
498
                _values: {
499
                        0: '"', // _("text")
500
                        1: '"', // "text"
501
                        2: "", // variable
502
                        8: '"' // 'text'
503
                },
504
                _args: {
505
                        4: '"', // :_("text")
506
                        5: '"', // :"text"
507
                        6: "", // :variable
508
                        7: "'"// :'text'
509
                },
510
                _tokenize: function(){
511
                        var pos, arg;
512
513
                        for(var i = 0, has = []; i < arguments.length; i++){
514
                                has[i] = (typeof arguments[i] != "undefined" && typeof arguments[i] == "string" && arguments[i]);
515
                        }
516
517
                        if(!this.key){
518
                                for(pos in this._values){
519
                                        if(has[pos]){
520
                                                this.key = this._values[pos] + arguments[pos] + this._values[pos];
521
                                                break;
522
                                        }
523
                                }
524
                        }else{
525
                                for(pos in this._args){
526
                                        if(has[pos]){
527
                                                var value = arguments[pos];
528
                                                if(this._args[pos] == "'"){
529
                                                        value = value.replace(/\\'/g, "'");
530
                                                }else if(this._args[pos] == '"'){
531
                                                        value = value.replace(/\\"/g, '"');
532
                                                }
533
                                                arg = [!this._args[pos], value];
534
                                                break;
535
                                        }
536
                                }
537
                                // Get a named filter
538
                                var fn = ddt.getFilter(arguments[3]);
539
                                if(!dojo.isFunction(fn)) throw new Error(arguments[3] + " is not registered as a filter");
540
                                this.filters.push([fn, arg]);
541
                        }
542
                },
543
                getExpression: function(){
544
                        return this.contents;
545
                },
546
                resolve: function(context){
547
                        if(typeof this.key == "undefined"){
548
                                return "";
549
                        }
550
551
                        var str = this.resolvePath(this.key, context);
552
553
                        for(var i = 0, filter; filter = this.filters[i]; i++){
554
                                // Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
555
                                // and [1][1] is either the variable name of the string content.
556
                                if(filter[1]){
557
                                        if(filter[1][0]){
558
                                                str = filter[0](str, this.resolvePath(filter[1][1], context));
559
                                        }else{
560
                                                str = filter[0](str, filter[1][1]);
561
                                        }
562
                                }else{
563
                                        str = filter[0](str);
564
                                }
565
                        }
566
567
                        return str;
568
                },
569
                resolvePath: function(path, context){
570
                        var current, parts;
571
                        var first = path.charAt(0);
572
                        var last = path.slice(-1);
573
                        if(!isNaN(parseInt(first))){
574
                                current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
575
                        }else if(first == '"' && first == last){
576
                                current = path.slice(1, -1);
577
                        }else{
578
                                if(path == "true"){ return true; }
579
                                if(path == "false"){ return false; }
580
                                if(path == "null" || path == "None"){ return null; }
581
                                parts = path.split(".");
582
                                current = context.get(parts[0]);
583
584
                                if(dojo.isFunction(current)){
585
                                        var self = context.getThis && context.getThis();
586
                                        if(current.alters_data){
587
                                                current = "";
588
                                        }else if(self){
589
                                                current = current.call(self);
590
                                        }else{
591
                                                current = "";
592
                                        }
593
                                }
594
595
                                for(var i = 1; i < parts.length; i++){
596
                                        var part = parts[i];
597
                                        if(current){
598
                                                var base = current;
599
                                                if(dojo.isObject(current) && part == "items" && typeof current[part] == "undefined"){
600
                                                        var items = [];
601
                                                        for(var key in current){
602
                                                                items.push([key, current[key]]);
603
                                                        }
604
                                                        current = items;
605
                                                        continue;
606
                                                }
607
608
                                                if(current.get && dojo.isFunction(current.get) && current.get.safe){
609
                                                        current = current.get(part);
610
                                                }else if(typeof current[part] == "undefined"){
611
                                                        current = current[part];
612
                                                        break;
613
                                                }else{
614
                                                        current = current[part];
615
                                                }
616
617
                                                if(dojo.isFunction(current)){
618
                                                        if(current.alters_data){
619
                                                                current = "";
620
                                                        }else{
621
                                                                current = current.call(base);
622
                                                        }
623
                                                }else if(current instanceof Date){
624
                                                        current = dd._Context.prototype._normalize(current);
625
                                                }
626
                                        }else{
627
                                                return "";
628
                                        }
629
                                }
630
                        }
631
                        return current;
632
                }
633
        });
634
635
        dd._TextNode = dd._Node = dojo.extend(function(/*Object*/ obj){
636
                // summary: Basic catch-all node
637
                this.contents = obj;
638
        },
639
        {
640
                set: function(data){
641
                        this.contents = data;
642
                        return this;
643
                },
644
                render: function(context, buffer){
645
                        // summary: Adds content onto the buffer
646
                        return buffer.concat(this.contents);
647
                },
648
                isEmpty: function(){
649
                        return !dojo.trim(this.contents);
650
                },
651
                clone: function(){ return this; }
652
        });
653
654
        dd._NodeList = dojo.extend(function(/*Node[]*/ nodes){
655
                // summary: Allows us to render a group of nodes
656
                this.contents = nodes || [];
657
                this.last = "";
658
        },
659
        {
660
                push: function(node){
661
                        // summary: Add a new node to the list
662
                        this.contents.push(node);
663
                        return this;
664
                },
665
                concat: function(nodes){
666
                        this.contents = this.contents.concat(nodes);
667
                        return this;
668
                },
669
                render: function(context, buffer){
670
                        // summary: Adds all content onto the buffer
671
                        for(var i = 0; i < this.contents.length; i++){
672
                                buffer = this.contents[i].render(context, buffer);
673
                                if(!buffer) throw new Error("Template must return buffer");
674
                        }
675
                        return buffer;
676
                },
677
                dummyRender: function(context){
678
                        return this.render(context, dd.Template.prototype.getBuffer()).toString();
679
                },
680
                unrender: function(){ return arguments[1]; },
681
                clone: function(){ return this; },
682
                rtrim: function(){
683
                        while(1){
684
                                i = this.contents.length - 1;
685
                                if(this.contents[i] instanceof dd._TextNode && this.contents[i].isEmpty()){
686
                                        this.contents.pop();
687
                                }else{
688
                                        break;
689
                                }
690
                        }
691
692
                        return this;
693
                }
694
        });
695
696
        dd._VarNode = dojo.extend(function(str){
697
                // summary: A node to be processed as a variable
698
                this.contents = new dd._Filter(str);
699
        },
700
        {
701
                render: function(context, buffer){
702
                        var str = this.contents.resolve(context);
703
                        if(!str.safe){
704
                                str = dd._base.escape("" + str);
705
                        }
706
                        return buffer.concat(str);
707
                }
708
        });
709
710
        dd._noOpNode = new function(){
711
                // summary: Adds a no-op node. Useful in custom tags
712
                this.render = this.unrender = function(){ return arguments[1]; }
713
                this.clone = function(){ return this; }
714
        }
715
716
        dd._Parser = dojo.extend(function(tokens){
717
                // summary: Parser used during initialization and for tag groups.
718
                this.contents = tokens;
719
        },
720
        {
721
                i: 0,
722
                parse: function(/*Array?*/ stop_at){
723
                        // summary: Turns tokens into nodes
724
                        // description: Steps into tags are they're found. Blocks use the parse object
725
                        //                to find their closing tag (the stop_at array). stop_at is inclusive, it
726
                        //                returns the node that matched.
727
                        var terminators = {}, token;
728
                        stop_at = stop_at || [];
729
                        for(var i = 0; i < stop_at.length; i++){
730
                                terminators[stop_at[i]] = true;
731
                        }
732
733
                        var nodelist = new dd._NodeList();
734
                        while(this.i < this.contents.length){
735
                                token = this.contents[this.i++];
736
                                if(typeof token == "string"){
737
                                        nodelist.push(new dd._TextNode(token));
738
                                }else{
739
                                        var type = token[0];
740
                                        var text = token[1];
741
                                        if(type == dd.TOKEN_VAR){
742
                                                nodelist.push(new dd._VarNode(text));
743
                                        }else if(type == dd.TOKEN_BLOCK){
744
                                                if(terminators[text]){
745
                                                        --this.i;
746
                                                        return nodelist;
747
                                                }
748
                                                var cmd = text.split(/\s+/g);
749
                                                if(cmd.length){
750
                                                        cmd = cmd[0];
751
                                                        var fn = ddt.getTag(cmd);
752
                                                        if(fn){
753
                                                                nodelist.push(fn(this, new dd.Token(type, text)));
754
                                                        }
755
                                                }
756
                                        }
757
                                }
758
                        }
759
760
                        if(stop_at.length){
761
                                throw new Error("Could not find closing tag(s): " + stop_at.toString());
762
                        }
763
764
                        this.contents.length = 0;
765
                        return nodelist;
766
                },
767
                next_token: function(){
768
                        // summary: Returns the next token in the list.
769
                        var token = this.contents[this.i++];
770
                        return new dd.Token(token[0], token[1]);
771
                },
772
                delete_first_token: function(){
773
                        this.i++;
774
                },
775
                skip_past: function(endtag){
776
                        while(this.i < this.contents.length){
777
                                var token = this.contents[this.i++];
778
                                if(token[0] == dd.TOKEN_BLOCK && token[1] == endtag){
779
                                        return;
780
                                }
781
                        }
782
                        throw new Error("Unclosed tag found when looking for " + endtag);
783
                },
784
                create_variable_node: function(expr){
785
                        return new dd._VarNode(expr);
786
                },
787
                create_text_node: function(expr){
788
                        return new dd._TextNode(expr || "");
789
                },
790
                getTemplate: function(file){
791
                        return new dd.Template(file);
792
                }
793
        });
794
795
        dd.register = {
796
                _registry: {
797
                        attributes: [],
798
                        tags: [],
799
                        filters: []
800
                },
801
                get: function(/*String*/ module, /*String*/ name){
802
                        var registry = dd.register._registry[module + "s"];
803
                        for(var i = 0, entry; entry = registry[i]; i++){
804
                                if(typeof entry[0] == "string"){
805
                                        if(entry[0] == name){
806
                                                return entry;
807
                                        }
808
                                }else if(name.match(entry[0])){
809
                                        return entry;
810
                                }
811
                        }
812
                },
813
                getAttributeTags: function(){
814
                        var tags = [];
815
                        var registry = dd.register._registry.attributes;
816
                        for(var i = 0, entry; entry = registry[i]; i++){
817
                                if(entry.length == 3){
818
                                        tags.push(entry);
819
                                }else{
820
                                        var fn = dojo.getObject(entry[1]);
821
                                        if(fn && dojo.isFunction(fn)){
822
                                                entry.push(fn);
823
                                                tags.push(entry);
824
                                        }
825
                                }
826
                        }
827
                        return tags;
828
                },
829
                _any: function(type, base, locations){
830
                        for(var path in locations){
831
                                for(var i = 0, fn; fn = locations[path][i]; i++){
832
                                        var key = fn;
833
                                        if(dojo.isArray(fn)){
834
                                                key = fn[0];
835
                                                fn = fn[1];
836
                                        }
837
                                        if(typeof key == "string"){
838
                                                if(key.substr(0, 5) == "attr:"){
839
                                                        var attr = fn;
840
                                                        if(attr.substr(0, 5) == "attr:"){
841
                                                                attr = attr.slice(5);
842
                                                        }
843
                                                        dd.register._registry.attributes.push([attr.toLowerCase(), base + "." + path + "." + attr]);
844
                                                }
845
                                                key = key.toLowerCase()
846
                                        }
847
                                        dd.register._registry[type].push([
848
                                                key,
849
                                                fn,
850
                                                base + "." + path
851
                                        ]);
852
                                }
853
                        }
854
                },
855
                tags: function(/*String*/ base, /*Object*/ locations){
856
                        dd.register._any("tags", base, locations);
857
                },
858
                filters: function(/*String*/ base, /*Object*/ locations){
859
                        dd.register._any("filters", base, locations);
860
                }
861
        }
862
863
        var escapeamp = /&/g;
864
        var escapelt = /</g;
865
        var escapegt = />/g;
866
        var escapeqt = /'/g;
867
        var escapedblqt = /"/g;
868
        dd._base.escape = function(value){
869
                // summary: Escapes a string's HTML
870
                return dd.mark_safe(value.replace(escapeamp, '&amp;').replace(escapelt, '&lt;').replace(escapegt, '&gt;').replace(escapedblqt, '&quot;').replace(escapeqt, '&#39;'));
871
        }
872
873
        dd._base.safe = function(value){
874
                if(typeof value == "string"){
875
                        value = new String(value);
876
                }
877
                if(typeof value == "object"){
878
                        value.safe = true;
879
                }
880
                return value;
881
        }
882
        dd.mark_safe = dd._base.safe;
883
884
        dd.register.tags("dojox.dtl.tag", {
885
                "date": ["now"],
886
                "logic": ["if", "for", "ifequal", "ifnotequal"],
887
                "loader": ["extends", "block", "include", "load", "ssi"],
888
                "misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
889
                "loop": ["cycle", "ifchanged", "regroup"]
890
        });
891
        dd.register.filters("dojox.dtl.filter", {
892
                "dates": ["date", "time", "timesince", "timeuntil"],
893
                "htmlstrings": ["linebreaks", "linebreaksbr", "removetags", "striptags"],
894
                "integers": ["add", "get_digit"],
895
                "lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
896
                "logic": ["default", "default_if_none", "divisibleby", "yesno"],
897
                "misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
898
                "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"]
899
        });
900
        dd.register.filters("dojox.dtl", {
901
                "_base": ["escape", "safe"]
902
        });
903
})();
904
905
}
906
907
if(!dojo._hasResource["dojox.dtl"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
908
dojo._hasResource["dojox.dtl"] = true;
909
dojo.provide("dojox.dtl");
910
911
912
}
913
914
if(!dojo._hasResource["dojox.dtl.Context"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
915
dojo._hasResource["dojox.dtl.Context"] = true;
916
dojo.provide("dojox.dtl.Context");
917
918
919
dojox.dtl.Context = dojo.extend(function(dict){
920
        this._this = {};
921
        dojox.dtl._Context.call(this, dict);
922
}, dojox.dtl._Context.prototype,
923
{
924
        getKeys: function(){
925
                var keys = [];
926
                for(var key in this){
927
                        if(this.hasOwnProperty(key) && key != "_this"){
928
                                keys.push(key);
929
                        }
930
                }
931
                return keys;
932
        },
933
        extend: function(/*dojox.dtl.Context|Object*/ obj){
934
                // summary: Returns a clone of this context object, with the items from the
935
                //                passed objecct mixed in.
936
                return  dojo.delegate(this, obj);
937
        },
938
        filter: function(/*dojox.dtl.Context|Object|String...*/ filter){
939
                // summary: Returns a clone of this context, only containing the items
940
                //                defined in the filter.
941
                var context = new dojox.dtl.Context();
942
                var keys = [];
943
                var i, arg;
944
                if(filter instanceof dojox.dtl.Context){
945
                        keys = filter.getKeys();
946
                }else if(typeof filter == "object"){
947
                        for(var key in filter){
948
                                keys.push(key);
949
                        }
950
                }else{
951
                        for(i = 0; arg = arguments[i]; i++){
952
                                if(typeof arg == "string"){
953
                                        keys.push(arg);
954
                                }
955
                        }
956
                }
957
958
                for(i = 0, key; key = keys[i]; i++){
959
                        context[key] = this[key];
960
                }
961
962
                return context;
963
        },
964
        setThis: function(/*Object*/ _this){
965
                this._this = _this;
966
        },
967
        getThis: function(){
968
                return this._this;
969
        },
970
        hasKey: function(key){
971
                if(this._getter){
972
                        var got = this._getter(key);
973
                        if(typeof got != "undefined"){
974
                                return true;
975
                        }
976
                }
977
978
                if(typeof this[key] != "undefined"){
979
                        return true;
980
                }
981
982
                return false;
983
        }
984
});
985
986
}
987
988
if(!dojo._hasResource["dojox.dtl.tag.logic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
989
dojo._hasResource["dojox.dtl.tag.logic"] = true;
990
dojo.provide("dojox.dtl.tag.logic");
991
992
993
994
(function(){
995
        var dd = dojox.dtl;
996
        var ddt = dd.text;
997
        var ddtl = dd.tag.logic;
998
999
        ddtl.IfNode = dojo.extend(function(bools, trues, falses, type){
1000
                this.bools = bools;
1001
                this.trues = trues;
1002
                this.falses = falses;
1003
                this.type = type;
1004
        },
1005
        {
1006
                render: function(context, buffer){
1007
                        var i, bool, ifnot, filter, value;
1008
                        if(this.type == "or"){
1009
                                for(i = 0; bool = this.bools[i]; i++){
1010
                                        ifnot = bool[0];
1011
                                        filter = bool[1];
1012
                                        value = filter.resolve(context);
1013
                                        if((value && !ifnot) || (ifnot && !value)){
1014
                                                if(this.falses){
1015
                                                        buffer = this.falses.unrender(context, buffer);
1016
                                                }
1017
                                                return (this.trues) ? this.trues.render(context, buffer, this) : buffer;
1018
                                        }
1019
                                }
1020
                                if(this.trues){
1021
                                        buffer = this.trues.unrender(context, buffer);
1022
                                }
1023
                                return (this.falses) ? this.falses.render(context, buffer, this) : buffer;
1024
                        }else{
1025
                                for(i = 0; bool = this.bools[i]; i++){
1026
                                        ifnot = bool[0];
1027
                                        filter = bool[1];
1028
                                        value = filter.resolve(context);
1029
                                        // If we ever encounter a false value
1030
                                        if(value == ifnot){
1031
                                                if(this.trues){
1032
                                                        buffer = this.trues.unrender(context, buffer);
1033
                                                }
1034
                                                return (this.falses) ? this.falses.render(context, buffer, this) : buffer;
1035
                                        }
1036
                                }
1037
                                if(this.falses){
1038
                                        buffer = this.falses.unrender(context, buffer);
1039
                                }
1040
                                return (this.trues) ? this.trues.render(context, buffer, this) : buffer;
1041
                        }
1042
                        return buffer;
1043
                },
1044
                unrender: function(context, buffer){
1045
                        buffer = (this.trues) ? this.trues.unrender(context, buffer) : buffer;
1046
                        buffer = (this.falses) ? this.falses.unrender(context, buffer) : buffer;
1047
                        return buffer;
1048
                },
1049
                clone: function(buffer){
1050
                        var trues = (this.trues) ? this.trues.clone(buffer) : null;
1051
                        var falses = (this.falses) ? this.falses.clone(buffer) : null;
1052
                        return new this.constructor(this.bools, trues, falses, this.type);
1053
                }
1054
        });
1055
1056
        ddtl.IfEqualNode = dojo.extend(function(var1, var2, trues, falses, negate){
1057
                this.var1 = new dd._Filter(var1);
1058
                this.var2 = new dd._Filter(var2);
1059
                this.trues = trues;
1060
                this.falses = falses;
1061
                this.negate = negate;
1062
        },
1063
        {
1064
                render: function(context, buffer){
1065
                        var var1 = this.var1.resolve(context);
1066
                        var var2 = this.var2.resolve(context);
1067
                        var1 = (typeof var1 != "undefined") ? var1 : "";
1068
                        var2 = (typeof var1 != "undefined") ? var2 : "";
1069
                        if((this.negate && var1 != var2) || (!this.negate && var1 == var2)){
1070
                                if(this.falses){
1071
                                        buffer = this.falses.unrender(context, buffer, this);
1072
                                }
1073
                                return (this.trues) ? this.trues.render(context, buffer, this) : buffer;
1074
                        }
1075
                        if(this.trues){
1076
                                buffer = this.trues.unrender(context, buffer, this);
1077
                        }
1078
                        return (this.falses) ? this.falses.render(context, buffer, this) : buffer;
1079
                },
1080
                unrender: function(context, buffer){
1081
                        return ddtl.IfNode.prototype.unrender.call(this, context, buffer);
1082
                },
1083
                clone: function(buffer){
1084
                        var trues = this.trues ? this.trues.clone(buffer) : null;
1085
                        var falses = this.falses ? this.falses.clone(buffer) : null;
1086
                        return new this.constructor(this.var1.getExpression(), this.var2.getExpression(), trues, falses, this.negate);
1087
                }
1088
        });
1089
1090
        ddtl.ForNode = dojo.extend(function(assign, loop, reversed, nodelist){
1091
                this.assign = assign;
1092
                this.loop = new dd._Filter(loop);
1093
                this.reversed = reversed;
1094
                this.nodelist = nodelist;
1095
                this.pool = [];
1096
        },
1097
        {
1098
                render: function(context, buffer){
1099
                        var i, j, k;
1100
                        var dirty = false;
1101
                        var assign = this.assign;
1102
1103
                        for(k = 0; k < assign.length; k++){
1104
                                if(typeof context[assign[k]] != "undefined"){
1105
                                        dirty = true;
1106
                                        context = context.push();
1107
                                        break;
1108
                                }
1109
                        }
1110
                        if(!dirty && context.forloop){
1111
                                dirty = true;
1112
                                context = context.push();
1113
                        }
1114
1115
                        var items = this.loop.resolve(context) || [];
1116
                        for(i = items.length; i < this.pool.length; i++){
1117
                                this.pool[i].unrender(context, buffer, this);
1118
                        }
1119
                        if(this.reversed){
1120
                                items = items.slice(0).reverse();
1121
                        }
1122
1123
                        var isObject = dojo.isObject(items) && !dojo.isArrayLike(items);
1124
                        var arred = [];
1125
                        if(isObject){
1126
                                for(var key in items){
1127
                                        arred.push(items[key]);
1128
                                }
1129
                        }else{
1130
                                arred = items;
1131
                        }
1132
1133
                        var forloop = context.forloop = {
1134
                                parentloop: context.get("forloop", {})
1135
                        };
1136
                        var j = 0;
1137
                        for(i = 0; i < arred.length; i++){
1138
                                var item = arred[i];
1139
1140
                                forloop.counter0 = j;
1141
                                forloop.counter = j + 1;
1142
                                forloop.revcounter0 = arred.length - j - 1;
1143
                                forloop.revcounter = arred.length - j;
1144
                                forloop.first = !j;
1145
                                forloop.last = (j == arred.length - 1);
1146
1147
                                if(assign.length > 1 && dojo.isArrayLike(item)){
1148
                                        if(!dirty){
1149
                                                dirty = true;
1150
                                                context = context.push();
1151
                                        }
1152
                                        var zipped = {};
1153
                                        for(k = 0; k < item.length && k < assign.length; k++){
1154
                                                zipped[assign[k]] = item[k];
1155
                                        }
1156
                                        dojo.mixin(context, zipped);
1157
                                }else{
1158
                                        context[assign[0]] = item;
1159
                                }
1160
1161
                                if(j + 1 > this.pool.length){
1162
                                        this.pool.push(this.nodelist.clone(buffer));
1163
                                }
1164
                                buffer = this.pool[j++].render(context, buffer, this);
1165
                        }
1166
1167
                        delete context.forloop;
1168
                        if(dirty){
1169
                                context = context.pop();
1170
                        }else{
1171
                                for(k = 0; k < assign.length; k++){
1172
                                        delete context[assign[k]];
1173
                                }
1174
                        }
1175
                        return buffer;
1176
                },
1177
                unrender: function(context, buffer){
1178
                        for(var i = 0, pool; pool = this.pool[i]; i++){
1179
                                buffer = pool.unrender(context, buffer);
1180
                        }
1181
                        return buffer;
1182
                },
1183
                clone: function(buffer){
1184
                        return new this.constructor(this.assign, this.loop.getExpression(), this.reversed, this.nodelist.clone(buffer));
1185
                }
1186
        });
1187
1188
        dojo.mixin(ddtl, {
1189
                if_: function(parser, token){
1190
                        var i, part, type, bools = [], parts = token.contents.split();
1191
                        parts.shift();
1192
                        token = parts.join(" ");
1193
                        parts = token.split(" and ");
1194
                        if(parts.length == 1){
1195
                                type = "or";
1196
                                parts = token.split(" or ");
1197
                        }else{
1198
                                type = "and";
1199
                                for(i = 0; i < parts.length; i++){
1200
                                        if(parts[i].indexOf(" or ") != -1){
1201
                                                // Note, since we split by and, this is the only place we need to error check
1202
                                                throw new Error("'if' tags can't mix 'and' and 'or'");
1203
                                        }
1204
                                }
1205
                        }
1206
                        for(i = 0; part = parts[i]; i++){
1207
                                var not = false;
1208
                                if(part.indexOf("not ") == 0){
1209
                                        part = part.slice(4);
1210
                                        not = true;
1211
                                }
1212
                                bools.push([not, new dd._Filter(part)]);
1213
                        }
1214
                        var trues = parser.parse(["else", "endif"]);
1215
                        var falses = false;
1216
                        var token = parser.next_token();
1217
                        if(token.contents == "else"){
1218
                                falses = parser.parse(["endif"]);
1219
                                parser.next_token();
1220
                        }
1221
                        return new ddtl.IfNode(bools, trues, falses, type);
1222
                },
1223
                _ifequal: function(parser, token, negate){
1224
                        var parts = token.split_contents();
1225
                        if(parts.length != 3){
1226
                                throw new Error(parts[0] + " takes two arguments");
1227
                        }
1228
                        var end = 'end' + parts[0];
1229
                        var trues = parser.parse(["else", end]);
1230
                        var falses = false;
1231
                        var token = parser.next_token();
1232
                        if(token.contents == "else"){
1233
                                falses = parser.parse([end]);
1234
                                parser.next_token();
1235
                        }
1236
                        return new ddtl.IfEqualNode(parts[1], parts[2], trues, falses, negate);
1237
                },
1238
                ifequal: function(parser, token){
1239
                        return ddtl._ifequal(parser, token);
1240
                },
1241
                ifnotequal: function(parser, token){
1242
                        return ddtl._ifequal(parser, token, true);
1243
                },
1244
                for_: function(parser, token){
1245
                        var parts = token.contents.split();
1246
                        if(parts.length < 4){
1247
                                throw new Error("'for' statements should have at least four words: " + token.contents);
1248
                        }
1249
                        var reversed = parts[parts.length - 1] == "reversed";
1250
                        var index = (reversed) ? -3 : -2;
1251
                        if(parts[parts.length + index] != "in"){
1252
                                throw new Error("'for' tag received an invalid argument: " + token.contents);
1253
                        }
1254
                        var loopvars = parts.slice(1, index).join(" ").split(/ *, */);
1255
                        for(var i = 0; i < loopvars.length; i++){
1256
                                if(!loopvars[i] || loopvars[i].indexOf(" ") != -1){
1257
                                        throw new Error("'for' tag received an invalid argument: " + token.contents);
1258
                                }
1259
                        }
1260
                        var nodelist = parser.parse(["endfor"]);
1261
                        parser.next_token();
1262
                        return new ddtl.ForNode(loopvars, parts[parts.length + index + 1], reversed, nodelist);
1263
                }
1264
        });
1265
})();
1266
1267
}
1268
1269
if(!dojo._hasResource["dojox.dtl.tag.loop"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1270
dojo._hasResource["dojox.dtl.tag.loop"] = true;
1271
dojo.provide("dojox.dtl.tag.loop");
1272
1273
1274
1275
1276
(function(){
1277
        var dd = dojox.dtl;
1278
        var ddtl = dd.tag.loop;
1279
1280
        ddtl.CycleNode = dojo.extend(function(cyclevars, name, text, shared){
1281
                this.cyclevars = cyclevars;
1282
                this.name = name;
1283
                this.contents = text;
1284
                this.shared = shared || {counter: -1, map: {}};
1285
        },
1286
        {
1287
                render: function(context, buffer){
1288
                        if(context.forloop && !context.forloop.counter0){
1289
                                this.shared.counter = -1;
1290
                        }
1291
1292
                        ++this.shared.counter;
1293
                        var value = this.cyclevars[this.shared.counter % this.cyclevars.length];
1294
1295
                        var map = this.shared.map;
1296
                        if(!map[value]){
1297
                                map[value] = new dd._Filter(value);
1298
                        }
1299
                        value = map[value].resolve(context, buffer);
1300
1301
                        if(this.name){
1302
                                context[this.name] = value;
1303
                        }
1304
                        this.contents.set(value);
1305
                        return this.contents.render(context, buffer);
1306
                },
1307
                unrender: function(context, buffer){
1308
                        return this.contents.unrender(context, buffer);
1309
                },
1310
                clone: function(buffer){
1311
                        return new this.constructor(this.cyclevars, this.name, this.contents.clone(buffer), this.shared);
1312
                }
1313
        });
1314
1315
        ddtl.IfChangedNode = dojo.extend(function(nodes, vars, shared){
1316
                this.nodes = nodes;
1317
                this._vars = vars;
1318
                this.shared = shared || {last: null, counter: 0};
1319
                this.vars = dojo.map(vars, function(item){
1320
                        return new dojox.dtl._Filter(item);
1321
                });
1322
        }, {
1323
                render: function(context, buffer){
1324
                        if(context.forloop){
1325
                                if(context.forloop.counter <= this.shared.counter){
1326
                                        this.shared.last = null;
1327
                                }
1328
                                this.shared.counter = context.forloop.counter;
1329
                        }
1330
1331
                        var change;
1332
                        if(this.vars.length){
1333
                                change = dojo.toJson(dojo.map(this.vars, function(item){
1334
                                        return item.resolve(context);
1335
                                }));
1336
                        }else{
1337
                                change = this.nodes.dummyRender(context, buffer);
1338
                        }
1339
1340
                        if(change != this.shared.last){
1341
                                var firstloop = (this.shared.last === null);
1342
                                this.shared.last = change;
1343
                                context = context.push();
1344
                                context.ifchanged = {firstloop: firstloop};
1345
                                buffer = this.nodes.render(context, buffer);
1346
                                context = context.pop();
1347
                        }else{
1348
                                buffer = this.nodes.unrender(context, buffer);
1349
                        }
1350
                        return buffer;
1351
                },
1352
                unrender: function(context, buffer){
1353
                        return this.nodes.unrender(context, buffer);
1354
                },
1355
                clone: function(buffer){
1356
                        return new this.constructor(this.nodes.clone(buffer), this._vars, this.shared);
1357
                }
1358
        });
1359
1360
        ddtl.RegroupNode = dojo.extend(function(expression, key, alias){
1361
                this._expression = expression;
1362
                this.expression = new dd._Filter(expression);
1363
                this.key = key;
1364
                this.alias = alias;
1365
        },
1366
        {
1367
                _push: function(container, grouper, stack){
1368
                        if(stack.length){
1369
                                container.push({ grouper: grouper, list: stack });
1370
                        }
1371
                },
1372
                render: function(context, buffer){
1373
                        context[this.alias] = [];
1374
                        var list = this.expression.resolve(context);
1375
                        if(list){
1376
                                var last = null;
1377
                                var stack = [];
1378
                                for(var i = 0; i < list.length; i++){
1379
                                        var id = list[i][this.key];
1380
                                        if(last !== id){
1381
                                                this._push(context[this.alias], last, stack);
1382
                                                last = id;
1383
                                                stack = [list[i]];
1384
                                        }else{
1385
                                                stack.push(list[i]);
1386
                                        }
1387
                                }
1388
                                this._push(context[this.alias], last, stack);
1389
                        }
1390
                        return buffer;
1391
                },
1392
                unrender: function(context, buffer){
1393
                        return buffer;
1394
                },
1395
                clone: function(context, buffer){
1396
                        return this;
1397
                }
1398
        });
1399
1400
        dojo.mixin(ddtl, {
1401
                cycle: function(parser, token){
1402
                        // summary: Cycle among the given strings each time this tag is encountered
1403
                        var args = token.split_contents();
1404
1405
                        if(args.length < 2){
1406
                                throw new Error("'cycle' tag requires at least two arguments");
1407
                        }
1408
1409
                        if(args[1].indexOf(",") != -1){
1410
                                var vars = args[1].split(",");
1411
                                args = [args[0]];
1412
                                for(var i = 0; i < vars.length; i++){
1413
                                        args.push('"' + vars[i] + '"');
1414
                                }
1415
                        }
1416
1417
                        if(args.length == 2){
1418
                                var name = args[args.length - 1];
1419
1420
                                if(!parser._namedCycleNodes){
1421
                                        throw new Error("No named cycles in template: '" + name + "' is not defined");
1422
                                }
1423
                                if(!parser._namedCycleNodes[name]){
1424
                                        throw new Error("Named cycle '" + name + "' does not exist");
1425
                                }
1426
1427
                        return parser._namedCycleNodes[name];
1428
                        }
1429
1430
                        if(args.length > 4 && args[args.length - 2] == "as"){
1431
                                var name = args[args.length - 1];
1432
1433
                                var node = new ddtl.CycleNode(args.slice(1, args.length - 2), name, parser.create_text_node());
1434
1435
                                if(!parser._namedCycleNodes){
1436
                                        parser._namedCycleNodes = {};
1437
                                }
1438
                                parser._namedCycleNodes[name] = node;
1439
                        }else{
1440
                                node = new ddtl.CycleNode(args.slice(1), null, parser.create_text_node());
1441
                        }
1442
1443
                        return node;
1444
                },
1445
                ifchanged: function(parser, token){
1446
                        var parts = token.contents.split();
1447
                        var nodes = parser.parse(["endifchanged"]);
1448
                        parser.delete_first_token();
1449
                        return new ddtl.IfChangedNode(nodes, parts.slice(1));
1450
                },
1451
                regroup: function(parser, token){
1452
                        var tokens = dojox.string.tokenize(token.contents, /(\s+)/g, function(spaces){
1453
                                return spaces;
1454
                        });
1455
                        if(tokens.length < 11 || tokens[tokens.length - 3] != "as" || tokens[tokens.length - 7] != "by"){
1456
                                throw new Error("Expected the format: regroup list by key as newList");
1457
                        }
1458
                        var expression = tokens.slice(2, -8).join("");
1459
                        var key = tokens[tokens.length - 5];
1460
                        var alias = tokens[tokens.length - 1];
1461
                        return new ddtl.RegroupNode(expression, key, alias);
1462
                }
1463
        });
1464
})();
1465
1466
}
1467
1468
if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1469
dojo._hasResource["dojo.date"] = true;
1470
dojo.provide("dojo.date");
1471
1472
/*=====
1473
dojo.date = {
1474
        // summary: Date manipulation utilities
1475
}
1476
=====*/
1477
1478
dojo.date.getDaysInMonth = function(/*Date*/dateObject){
1479
        //        summary:
1480
        //                Returns the number of days in the month used by dateObject
1481
        var month = dateObject.getMonth();
1482
        var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1483
        if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
1484
        return days[month]; // Number
1485
}
1486
1487
dojo.date.isLeapYear = function(/*Date*/dateObject){
1488
        //        summary:
1489
        //                Determines if the year of the dateObject is a leap year
1490
        //        description:
1491
        //                Leap years are years with an additional day YYYY-02-29, where the
1492
        //                year number is a multiple of four with the following exception: If
1493
        //                a year is a multiple of 100, then it is only a leap year if it is
1494
        //                also a multiple of 400. For example, 1900 was not a leap year, but
1495
        //                2000 is one.
1496
1497
        var year = dateObject.getFullYear();
1498
        return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
1499
}
1500
1501
// FIXME: This is not localized
1502
dojo.date.getTimezoneName = function(/*Date*/dateObject){
1503
        //        summary:
1504
        //                Get the user's time zone as provided by the browser
1505
        // dateObject:
1506
        //                Needed because the timezone may vary with time (daylight savings)
1507
        //        description:
1508
        //                Try to get time zone info from toString or toLocaleString method of
1509
        //                the Date object -- UTC offset is not a time zone.  See
1510
        //                http://www.twinsun.com/tz/tz-link.htm Note: results may be
1511
        //                inconsistent across browsers.
1512
1513
        var str = dateObject.toString(); // Start looking in toString
1514
        var tz = ''; // The result -- return empty string if nothing found
1515
        var match;
1516
1517
        // First look for something in parentheses -- fast lookup, no regex
1518
        var pos = str.indexOf('(');
1519
        if(pos > -1){
1520
                tz = str.substring(++pos, str.indexOf(')'));
1521
        }else{
1522
                // If at first you don't succeed ...
1523
                // If IE knows about the TZ, it appears before the year
1524
                // Capital letters or slash before a 4-digit year
1525
                // at the end of string
1526
                var pat = /([A-Z\/]+) \d{4}$/;
1527
                if((match = str.match(pat))){
1528
                        tz = match[1];
1529
                }else{
1530
                // Some browsers (e.g. Safari) glue the TZ on the end
1531
                // of toLocaleString instead of putting it in toString
1532
                        str = dateObject.toLocaleString();
1533
                        // Capital letters or slash -- end of string,
1534
                        // after space
1535
                        pat = / ([A-Z\/]+)$/;
1536
                        if((match = str.match(pat))){
1537
                                tz = match[1];
1538
                        }
1539
                }
1540
        }
1541
1542
        // Make sure it doesn't somehow end up return AM or PM
1543
        return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
1544
}
1545
1546
// Utility methods to do arithmetic calculations with Dates
1547
1548
dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
1549
        //        summary:
1550
        //                Compare two date objects by date, time, or both.
1551
        //        description:
1552
        //          Returns 0 if equal, positive if a > b, else negative.
1553
        //        date1:
1554
        //                Date object
1555
        //        date2:
1556
        //                Date object.  If not specified, the current Date is used.
1557
        //        portion:
1558
        //                A string indicating the "date" or "time" portion of a Date object.
1559
        //                Compares both "date" and "time" by default.  One of the following:
1560
        //                "date", "time", "datetime"
1561
1562
        // Extra step required in copy for IE - see #3112
1563
        date1 = new Date(+date1);
1564
        date2 = new Date(+(date2 || new Date()));
1565
1566
        if(portion == "date"){
1567
                // Ignore times and compare dates.
1568
                date1.setHours(0, 0, 0, 0);
1569
                date2.setHours(0, 0, 0, 0);
1570
        }else if(portion == "time"){
1571
                // Ignore dates and compare times.
1572
                date1.setFullYear(0, 0, 0);
1573
                date2.setFullYear(0, 0, 0);
1574
        }
1575
1576
        if(date1 > date2){ return 1; } // int
1577
        if(date1 < date2){ return -1; } // int
1578
        return 0; // int
1579
};
1580
1581
dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
1582
        //        summary:
1583
        //                Add to a Date in intervals of different size, from milliseconds to years
1584
        //        date: Date
1585
        //                Date object to start with
1586
        //        interval:
1587
        //                A string representing the interval.  One of the following:
1588
        //                        "year", "month", "day", "hour", "minute", "second",
1589
        //                        "millisecond", "quarter", "week", "weekday"
1590
        //        amount:
1591
        //                How much to add to the date.
1592
1593
        var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112)
1594
        var fixOvershoot = false;
1595
        var property = "Date";
1596
1597
        switch(interval){
1598
                case "day":
1599
                        break;
1600
                case "weekday":
1601
                        //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true.  see dojo.cldr.supplemental
1602
1603
                        // Divide the increment time span into weekspans plus leftover days
1604
                        // e.g., 8 days is one 5-day weekspan / and two leftover days
1605
                        // Can't have zero leftover days, so numbers divisible by 5 get
1606
                        // a days value of 5, and the remaining days make up the number of weeks
1607
                        var days, weeks;
1608
                        var mod = amount % 5;
1609
                        if(!mod){
1610
                                days = (amount > 0) ? 5 : -5;
1611
                                weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
1612
                        }else{
1613
                                days = mod;
1614
                                weeks = parseInt(amount/5);
1615
                        }
1616
                        // Get weekday value for orig date param
1617
                        var strt = date.getDay();
1618
                        // Orig date is Sat / positive incrementer
1619
                        // Jump over Sun
1620
                        var adj = 0;
1621
                        if(strt == 6 && amount > 0){
1622
                                adj = 1;
1623
                        }else if(strt == 0 && amount < 0){
1624
                        // Orig date is Sun / negative incrementer
1625
                        // Jump back over Sat
1626
                                adj = -1;
1627
                        }
1628
                        // Get weekday val for the new date
1629
                        var trgt = strt + days;
1630
                        // New date is on Sat or Sun
1631
                        if(trgt == 0 || trgt == 6){
1632
                                adj = (amount > 0) ? 2 : -2;
1633
                        }
1634
                        // Increment by number of weeks plus leftover days plus
1635
                        // weekend adjustments
1636
                        amount = (7 * weeks) + days + adj;
1637
                        break;
1638
                case "year":
1639
                        property = "FullYear";
1640
                        // Keep increment/decrement from 2/29 out of March
1641
                        fixOvershoot = true;
1642
                        break;
1643
                case "week":
1644
                        amount *= 7;
1645
                        break;
1646
                case "quarter":
1647
                        // Naive quarter is just three months
1648
                        amount *= 3;
1649
                        // fallthrough...
1650
                case "month":
1651
                        // Reset to last day of month if you overshoot
1652
                        fixOvershoot = true;
1653
                        property = "Month";
1654
                        break;
1655
//                case "hour":
1656
//                case "minute":
1657
//                case "second":
1658
//                case "millisecond":
1659
                default:
1660
                        property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
1661
        }
1662
1663
        if(property){
1664
                sum["set"+property](sum["get"+property]()+amount);
1665
        }
1666
1667
        if(fixOvershoot && (sum.getDate() < date.getDate())){
1668
                sum.setDate(0);
1669
        }
1670
1671
        return sum; // Date
1672
};
1673
1674
dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
1675
        //        summary:
1676
        //                Get the difference in a specific unit of time (e.g., number of
1677
        //                months, weeks, days, etc.) between two dates, rounded to the
1678
        //                nearest integer.
1679
        //        date1:
1680
        //                Date object
1681
        //        date2:
1682
        //                Date object.  If not specified, the current Date is used.
1683
        //        interval:
1684
        //                A string representing the interval.  One of the following:
1685
        //                        "year", "month", "day", "hour", "minute", "second",
1686
        //                        "millisecond", "quarter", "week", "weekday"
1687
        //                Defaults to "day".
1688
1689
        date2 = date2 || new Date();
1690
        interval = interval || "day";
1691
        var yearDiff = date2.getFullYear() - date1.getFullYear();
1692
        var delta = 1; // Integer return value
1693
1694
        switch(interval){
1695
                case "quarter":
1696
                        var m1 = date1.getMonth();
1697
                        var m2 = date2.getMonth();
1698
                        // Figure out which quarter the months are in
1699
                        var q1 = Math.floor(m1/3) + 1;
1700
                        var q2 = Math.floor(m2/3) + 1;
1701
                        // Add quarters for any year difference between the dates
1702
                        q2 += (yearDiff * 4);
1703
                        delta = q2 - q1;
1704
                        break;
1705
                case "weekday":
1706
                        var days = Math.round(dojo.date.difference(date1, date2, "day"));
1707
                        var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
1708
                        var mod = days % 7;
1709
1710
                        // Even number of weeks
1711
                        if(mod == 0){
1712
                                days = weeks*5;
1713
                        }else{
1714
                                // Weeks plus spare change (< 7 days)
1715
                                var adj = 0;
1716
                                var aDay = date1.getDay();
1717
                                var bDay = date2.getDay();
1718
1719
                                weeks = parseInt(days/7);
1720
                                mod = days % 7;
1721
                                // Mark the date advanced by the number of
1722
                                // round weeks (may be zero)
1723
                                var dtMark = new Date(date1);
1724
                                dtMark.setDate(dtMark.getDate()+(weeks*7));
1725
                                var dayMark = dtMark.getDay();
1726
1727
                                // Spare change days -- 6 or less
1728
                                if(days > 0){
1729
                                        switch(true){
1730
                                                // Range starts on Sat
1731
                                                case aDay == 6:
1732
                                                        adj = -1;
1733
                                                        break;
1734
                                                // Range starts on Sun
1735
                                                case aDay == 0:
1736
                                                        adj = 0;
1737
                                                        break;
1738
                                                // Range ends on Sat
1739
                                                case bDay == 6:
1740
                                                        adj = -1;
1741
                                                        break;
1742
                                                // Range ends on Sun
1743
                                                case bDay == 0:
1744
                                                        adj = -2;
1745
                                                        break;
1746
                                                // Range contains weekend
1747
                                                case (dayMark + mod) > 5:
1748
                                                        adj = -2;
1749
                                        }
1750
                                }else if(days < 0){
1751
                                        switch(true){
1752
                                                // Range starts on Sat
1753
                                                case aDay == 6:
1754
                                                        adj = 0;
1755
                                                        break;
1756
                                                // Range starts on Sun
1757
                                                case aDay == 0:
1758
                                                        adj = 1;
1759
                                                        break;
1760
                                                // Range ends on Sat
1761
                                                case bDay == 6:
1762
                                                        adj = 2;
1763
                                                        break;
1764
                                                // Range ends on Sun
1765
                                                case bDay == 0:
1766
                                                        adj = 1;
1767
                                                        break;
1768
                                                // Range contains weekend
1769
                                                case (dayMark + mod) < 0:
1770
                                                        adj = 2;
1771
                                        }
1772
                                }
1773
                                days += adj;
1774
                                days -= (weeks*2);
1775
                        }
1776
                        delta = days;
1777
                        break;
1778
                case "year":
1779
                        delta = yearDiff;
1780
                        break;
1781
                case "month":
1782
                        delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
1783
                        break;
1784
                case "week":
1785
                        // Truncate instead of rounding
1786
                        // Don't use Math.floor -- value may be negative
1787
                        delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
1788
                        break;
1789
                case "day":
1790
                        delta /= 24;
1791
                        // fallthrough
1792
                case "hour":
1793
                        delta /= 60;
1794
                        // fallthrough
1795
                case "minute":
1796
                        delta /= 60;
1797
                        // fallthrough
1798
                case "second":
1799
                        delta /= 1000;
1800
                        // fallthrough
1801
                case "millisecond":
1802
                        delta *= date2.getTime() - date1.getTime();
1803
        }
1804
1805
        // Round for fractional values and DST leaps
1806
        return Math.round(delta); // Number (integer)
1807
};
1808
1809
}
1810
1811
if(!dojo._hasResource["dojox.date.php"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1812
dojo._hasResource["dojox.date.php"] = true;
1813
dojo.provide("dojox.date.php");
1814
1815
1816
1817
dojox.date.php.format = function(/*Date*/ date, /*String*/ format){
1818
        // summary: Get a formatted string for a given date object
1819
        var df = new dojox.date.php.DateFormat(format);
1820
        return df.format(date);
1821
}
1822
1823
dojox.date.php.DateFormat = function(/*String*/ format){
1824
        // summary: Format the internal date object
1825
        if(!this.regex){
1826
                var keys = [];
1827
                for(var key in this.constructor.prototype){
1828
                        if(dojo.isString(key) && key.length == 1 && dojo.isFunction(this[key])){
1829
                                keys.push(key);
1830
                        }
1831
                }
1832
                this.constructor.prototype.regex = new RegExp("(?:(\\\\.)|([" + keys.join("") + "]))", "g");
1833
        }
1834
1835
        var replacements = [];
1836
1837
        this.tokens = dojox.string.tokenize(format, this.regex, function(escape, token, i){
1838
                if(token){
1839
                        replacements.push([i, token]);
1840
                        return token;
1841
                }
1842
                if(escape){
1843
                        return escape.charAt(1);
1844
                }
1845
        });
1846
1847
        this.replacements = replacements;
1848
}
1849
dojo.extend(dojox.date.php.DateFormat, {
1850
        weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
1851
        weekdays_3: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
1852
        months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1853
        months_3: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1854
        monthdays: [31,28,31,30,31,30,31,31,30,31,30,31],
1855
1856
        format: function(/*Date*/ date){
1857
                this.date = date;
1858
                for(var i = 0, replacement; replacement = this.replacements[i]; i++){
1859
                        this.tokens[replacement[0]] = this[replacement[1]]();
1860
                }
1861
                return this.tokens.join("");
1862
        },
1863
1864
        // Day
1865
1866
        d: function(){
1867
                // summary: Day of the month, 2 digits with leading zeros
1868
                var j = this.j();
1869
                return (j.length == 1) ? "0" + j : j;
1870
        },
1871
1872
        D: function(){
1873
                // summary: A textual representation of a day, three letters
1874
                return this.weekdays_3[this.date.getDay()];
1875
        },
1876
1877
        j: function(){
1878
                // summary: Day of the month without leading zeros
1879
                return this.date.getDate() + "";
1880
        },
1881
1882
        l: function(){
1883
                // summary: A full textual representation of the day of the week
1884
                return this.weekdays[this.date.getDay()];
1885
        },
1886
1887
        N: function(){
1888
                // summary: ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
1889
                var w = this.w();
1890
                return (!w) ? 7 : w;
1891
        },
1892
1893
        S: function(){
1894
                // summary: English ordinal suffix for the day of the month, 2 characters
1895
                switch(this.date.getDate()){
1896
                        case 11: case 12: case 13: return "th";
1897
                        case 1: case 21: case 31: return "st";
1898
                        case 2: case 22: return "nd";
1899
                        case 3: case 23: return "rd";
1900
                        default: return "th";
1901
                }
1902
        },
1903
1904
        w: function(){
1905
                // summary: Numeric representation of the day of the week
1906
                return this.date.getDay() + "";
1907
        },
1908
1909
        z: function(){
1910
                // summary: The day of the year (starting from 0)
1911
                var millis = this.date.getTime() - new Date(this.date.getFullYear(), 0, 1).getTime();
1912
                return Math.floor(millis/86400000) + "";
1913
        },
1914
1915
        // Week
1916
1917
        W: function(){
1918
                // summary: ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0)
1919
                var week;
1920
                var jan1_w = new Date(this.date.getFullYear(), 0, 1).getDay() + 1;
1921
                var w = this.date.getDay() + 1;
1922
                var z = parseInt(this.z());
1923
1924
                if(z <= (8 - jan1_w) && jan1_w > 4){
1925
                        var last_year = new Date(this.date.getFullYear() - 1, this.date.getMonth(), this.date.getDate());
1926
                        if(jan1_w == 5 || (jan1_w == 6 && dojo.date.isLeapYear(last_year))){
1927
                                week = 53;
1928
                        }else{
1929
                                week = 52;
1930
                        }
1931
                }else{
1932
                        var i;
1933
                        if(Boolean(this.L())){
1934
                                i = 366;
1935
                        }else{
1936
                                i = 365;
1937
                        }
1938
                        if((i - z) < (4 - w)){
1939
                                week = 1;
1940
                        }else{
1941
                                var j = z + (7 - w) + (jan1_w - 1);
1942
                                week = Math.ceil(j / 7);
1943
                                if(jan1_w > 4){
1944
                                        --week;
1945
                                }
1946
                        }
1947
                }
1948
1949
                return week;
1950
        },
1951
1952
        // Month
1953
1954
        F: function(){
1955
                // summary: A full textual representation of a month, such as January or March
1956
                return this.months[this.date.getMonth()];
1957
        },
1958
1959
        m: function(){
1960
                // summary: Numeric representation of a month, with leading zeros
1961
                var n = this.n();
1962
                return (n.length == 1) ? "0" + n : n;
1963
        },
1964
1965
        M: function(){
1966
                // summary: A short textual representation of a month, three letters
1967
                return this.months_3[this.date.getMonth()];
1968
        },
1969
1970
        n: function(){
1971
                // summary: Numeric representation of a month, without leading zeros
1972
                return this.date.getMonth() + 1 + "";
1973
        },
1974
1975
        t: function(){
1976
                // summary: Number of days in the given month
1977
                return (Boolean(this.L()) && this.date.getMonth() == 1) ? 29 : this.monthdays[this.getMonth()];
1978
        },
1979
1980
        // Year
1981
1982
        L: function(){
1983
                // summary: Whether it's a leap year
1984
                return (dojo.date.isLeapYear(this.date)) ? "1" : "0";
1985
        },
1986
1987
        o: function(){
1988
                // summary:
1989
                //                ISO-8601 year number. This has the same value as Y, except that if
1990
                //                the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
1991
                // TODO: Figure out what this means
1992
        },
1993
1994
        Y: function(){
1995
                // summary: A full numeric representation of a year, 4 digits
1996
                return this.date.getFullYear() + "";
1997
        },
1998
1999
        y: function(){
2000
                // summary: A two digit representation of a year
2001
                return this.Y().slice(-2);
2002
        },
2003
2004
        // Time
2005
2006
        a: function(){
2007
                // summary: Lowercase Ante meridiem and Post meridiem
2008
                return this.date.getHours() >= 12 ? "pm" : "am";
2009
        },
2010
2011
        b: function(){
2012
                // summary: Uppercase Ante meridiem and Post meridiem
2013
                return this.a().toUpperCase();
2014
        },
2015
2016
        B: function(){
2017
                // summary:
2018
                //        Swatch Internet time
2019
                //        A day is 1,000 beats. All time is measured from GMT + 1
2020
                var off = this.date.getTimezoneOffset() + 60;
2021
                var secs = (this.date.getHours() * 3600) + (this.date.getMinutes() * 60) + this.getSeconds() + (off * 60);
2022
                var beat = Math.abs(Math.floor(secs / 86.4) % 1000) + "";
2023
                while(beat.length <  2) beat = "0" + beat;
2024
                return beat;
2025
        },
2026
2027
        g: function(){
2028
                // summary: 12-hour format of an hour without leading zeros
2029
                return (this.date.getHours() > 12) ? this.date.getHours() - 12 + "" : this.date.getHours() + "";
2030
        },
2031
2032
        G: function(){
2033
                // summary: 24-hour format of an hour without leading zeros
2034
                return this.date.getHours() + "";
2035
        },
2036
2037
        h: function(){
2038
                // summary: 12-hour format of an hour with leading zeros
2039
                var g = this.g();
2040
                return (g.length == 1) ? "0" + g : g;
2041
        },
2042
2043
        H: function(){
2044
                // summary: 24-hour format of an hour with leading zeros
2045
                var G = this.G();
2046
                return (G.length == 1) ? "0" + G : G;
2047
        },
2048
2049
        i: function(){
2050
                // summary: Minutes with leading zeros
2051
                var mins = this.date.getMinutes() + "";
2052
                return (mins.length == 1) ? "0" + mins : mins;
2053
        },
2054
2055
        s: function(){
2056
                // summary: Seconds, with leading zeros
2057
                var secs = this.date.getSeconds() + "";
2058
                return (secs.length == 1) ? "0" + secs : secs;
2059
        },
2060
2061
        // Timezone
2062
2063
        e: function(){
2064
                // summary: Timezone identifier (added in PHP 5.1.0)
2065
                return dojo.date.getTimezoneName(this.date);
2066
        },
2067
2068
        I: function(){
2069
                // summary: Whether or not the date is in daylight saving time
2070
                // TODO: Can dojo.date do this?
2071
        },
2072
2073
        O: function(){
2074
                // summary: Difference to Greenwich time (GMT) in hours
2075
                var off = Math.abs(this.date.getTimezoneOffset());
2076
                var hours = Math.floor(off / 60) + "";
2077
                var mins = (off % 60) + "";
2078
                if(hours.length == 1) hours = "0" + hours;
2079
                if(mins.length == 1) hours = "0" + mins;
2080
                return ((this.date.getTimezoneOffset() < 0) ? "+" : "-") + hours + mins;
2081
        },
2082
2083
        P: function(){
2084
                // summary: Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3)
2085
                var O = this.O();
2086
                return O.substring(0, 2) + ":" + O.substring(2, 4);
2087
        },
2088
2089
        T: function(){
2090
                // summary: Timezone abbreviation
2091
2092
                // Guess...
2093
                return this.e().substring(0, 3);
2094
        },
2095
2096
        Z: function(){
2097
                // summary:
2098
                //                Timezone offset in seconds. The offset for timezones west of UTC is always negative,
2099
                //                and for those east of UTC is always positive.
2100
                return this.date.getTimezoneOffset() * -60;
2101
        },
2102
2103
        // Full Date/Time
2104
2105
        c: function(){
2106
                // summary: ISO 8601 date (added in PHP 5)
2107
                return this.Y() + "-" + this.m() + "-" + this.d() + "T" + this.h() + ":" + this.i() + ":" + this.s() + this.P();
2108
        },
2109
2110
        r: function(){
2111
                // summary: RFC 2822 formatted date
2112
                return this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() + " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
2113
        },
2114
2115
        U: function(){
2116
                // summary: Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
2117
                return Math.floor(this.date.getTime() / 1000);
2118
        }
2119
2120
});
2121
2122
}
2123
2124
if(!dojo._hasResource["dojox.dtl.utils.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2125
dojo._hasResource["dojox.dtl.utils.date"] = true;
2126
dojo.provide("dojox.dtl.utils.date");
2127
2128
2129
2130
dojox.dtl.utils.date.DateFormat = function(/*String*/ format){
2131
        dojox.date.php.DateFormat.call(this, format);
2132
}
2133
dojo.extend(dojox.dtl.utils.date.DateFormat, dojox.date.php.DateFormat.prototype, {
2134
        f: function(){
2135
                // summary:
2136
                //                Time, in 12-hour hours and minutes, with minutes left off if they're zero.
2137
                // description:
2138
                //                Examples: '1', '1:30', '2:05', '2'
2139
                //                Proprietary extension.
2140
                return (!this.date.getMinutes()) ? this.g() : this.g() + ":" + this.i();
2141
        },
2142
        N: function(){
2143
                // summary: Month abbreviation in Associated Press style. Proprietary extension.
2144
                return dojox.dtl.utils.date._months_ap[this.date.getMonth()];
2145
        },
2146
        P: function(){
2147
                // summary:
2148
                //                Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off
2149
                //                if they're zero and the strings 'midnight' and 'noon' if appropriate.
2150
                // description:
2151
                //                Examples: '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.'
2152
                //                Proprietary extension.
2153
                if(!this.date.getMinutes() && !this.date.getHours()){
2154
                        return 'midnight';
2155
                }
2156
                if(!this.date.getMinutes() && this.date.getHours() == 12){
2157
                        return 'noon';
2158
                }
2159
                return this.f() + " " + this.a();
2160
        }
2161
});
2162
2163
dojo.mixin(dojox.dtl.utils.date, {
2164
        format: function(/*Date*/ date, /*String*/ format){
2165
                var df = new dojox.dtl.utils.date.DateFormat(format);
2166
                return df.format(date);
2167
        },
2168
        timesince: function(d, now){
2169
                // summary:
2170
                //                Takes two datetime objects and returns the time between then and now
2171
                //                as a nicely formatted string, e.g "10 minutes"
2172
                // description:
2173
                //                Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
2174
                if(!(d instanceof Date)){
2175
                        d = new Date(d.year, d.month, d.day);
2176
                }
2177
                if(!now){
2178
                        now = new Date();
2179
                }
2180
2181
                var delta = Math.abs(now.getTime() - d.getTime());
2182
                for(var i = 0, chunk; chunk = dojox.dtl.utils.date._chunks[i]; i++){
2183
                        var count = Math.floor(delta / chunk[0]);
2184
                        if(count) break;
2185
                }
2186
                return count + " " + chunk[1](count);
2187
        },
2188
        _chunks: [
2189
                [60 * 60 * 24 * 365 * 1000, function(n){ return (n == 1) ? 'year' : 'years'; }],
2190
                [60 * 60 * 24 * 30 * 1000, function(n){ return (n == 1) ? 'month' : 'months'; }],
2191
                [60 * 60 * 24 * 7 * 1000, function(n){ return (n == 1) ? 'week' : 'weeks'; }],
2192
                [60 * 60 * 24 * 1000, function(n){ return (n == 1) ? 'day' : 'days'; }],
2193
                [60 * 60 * 1000, function(n){ return (n == 1) ? 'hour' : 'hours'; }],
2194
                [60 * 1000, function(n){ return (n == 1) ? 'minute' : 'minutes'; }]
2195
        ],
2196
        _months_ap: ["Jan.", "Feb.", "March", "April", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."]
2197
});
2198
2199
}
2200
2201
if(!dojo._hasResource["dojox.dtl.tag.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2202
dojo._hasResource["dojox.dtl.tag.date"] = true;
2203
dojo.provide("dojox.dtl.tag.date");
2204
2205
2206
2207
2208
dojox.dtl.tag.date.NowNode = function(format, node){
2209
        this._format = format;
2210
        this.format = new dojox.dtl.utils.date.DateFormat(format);
2211
        this.contents = node;
2212
}
2213
dojo.extend(dojox.dtl.tag.date.NowNode, {
2214
        render: function(context, buffer){
2215
                this.contents.set(this.format.format(new Date()));
2216
                return this.contents.render(context, buffer);
2217
        },
2218
        unrender: function(context, buffer){
2219
                return this.contents.unrender(context, buffer);
2220
        },
2221
        clone: function(buffer){
2222
                return new this.constructor(this._format, this.contents.clone(buffer));
2223
        }
2224
});
2225
2226
dojox.dtl.tag.date.now = function(parser, token){
2227
        // Split by either :" or :'
2228
        var parts = token.split_contents();
2229
        if(parts.length != 2){
2230
                throw new Error("'now' statement takes one argument");
2231
        }
2232
        return new dojox.dtl.tag.date.NowNode(parts[1].slice(1, -1), parser.create_text_node());
2233
}
2234
2235
}
2236
2237
if(!dojo._hasResource["dojox.dtl.tag.loader"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2238
dojo._hasResource["dojox.dtl.tag.loader"] = true;
2239
dojo.provide("dojox.dtl.tag.loader");
2240
2241
2242
2243
(function(){
2244
        var dd = dojox.dtl;
2245
        var ddtl = dd.tag.loader;
2246
2247
        ddtl.BlockNode = dojo.extend(function(name, nodelist){
2248
                this.name = name;
2249
                this.nodelist = nodelist; // Can be overridden
2250
        },
2251
        {
2252
                "super": function(){
2253
                        if(this.parent){
2254
                                var html = this.parent.nodelist.dummyRender(this.context, null, true);
2255
                                if(typeof html == "string"){
2256
                                        html = new String(html);
2257
                                }
2258
                                html.safe = true;
2259
                                return html;
2260
                        }
2261
                        return '';
2262
                },
2263
                render: function(context, buffer){
2264
                        var name = this.name;
2265
                        var nodelist = this.nodelist;
2266
                        var parent;
2267
                        if(buffer.blocks){
2268
                                var block = buffer.blocks[name];
2269
                                if(block){
2270
                                        parent = block.parent;
2271
                                        nodelist = block.nodelist;
2272
                                        block.used = true;
2273
                                }
2274
                        }
2275
2276
                        this.rendered = nodelist;
2277
2278
                        context = context.push();
2279
                        this.context = context;
2280
                        this.parent = null;
2281
                        if(nodelist != this.nodelist){
2282
                                this.parent = this;
2283
                        }
2284
                        context.block = this;
2285
2286
                        if(buffer.getParent){
2287
                                var bufferParent = buffer.getParent();
2288
                                var setParent = dojo.connect(buffer, "onSetParent", function(node, up, root){
2289
                                        if(up && root){
2290
                                                buffer.setParent(bufferParent);
2291
                                        }
2292
                                });
2293
                        }
2294
                        buffer = nodelist.render(context, buffer, this);
2295
                        setParent && dojo.disconnect(setParent);
2296
                        context = context.pop();
2297
                        return buffer;
2298
                },
2299
                unrender: function(context, buffer){
2300
                        return this.rendered.unrender(context, buffer);
2301
                },
2302
                clone: function(buffer){
2303
                        return new this.constructor(this.name, this.nodelist.clone(buffer));
2304
                },
2305
                toString: function(){ return "dojox.dtl.tag.loader.BlockNode"; }
2306
        });
2307
2308
        ddtl.ExtendsNode = dojo.extend(function(getTemplate, nodelist, shared, parent, key){
2309
                this.getTemplate = getTemplate;
2310
                this.nodelist = nodelist;
2311
                this.shared = shared;
2312
                this.parent = parent;
2313
                this.key = key;
2314
        },
2315
        {
2316
                parents: {},
2317
                getParent: function(context){
2318
                        var parent = this.parent;
2319
                        if(!parent){
2320
                                var string;
2321
                                parent = this.parent = context.get(this.key, false);
2322
                                if(!parent){
2323
                                        throw new Error("extends tag used a variable that did not resolve");
2324
                                }
2325
                                if(typeof parent == "object"){
2326
                                        var url = parent.url || parent.templatePath;
2327
                                        if(parent.shared){
2328
                                                this.shared = true;
2329
                                        }
2330
                                        if(url){
2331
                                                parent = this.parent = url.toString();
2332
                                        }else if(parent.templateString){
2333
                                                // Allow the builder's string interning to work
2334
                                                string = parent.templateString;
2335
                                                parent = this.parent = " ";
2336
                                        }else{
2337
                                                parent = this.parent = this.parent.toString();
2338
                                        }
2339
                                }
2340
                                if(parent && parent.indexOf("shared:") === 0){
2341
                                        this.shared = true;
2342
                                        parent = this.parent = parent.substring(7, parent.length);
2343
                                }
2344
                        }
2345
                        if(!parent){
2346
                                throw new Error("Invalid template name in 'extends' tag.");
2347
                        }
2348
                        if(parent.render){
2349
                                return parent;
2350
                        }
2351
                        if(this.parents[parent]){
2352
                                return this.parents[parent];
2353
                        }
2354
                        this.parent = this.getTemplate(string || dojox.dtl.text.getTemplateString(parent));
2355
                        if(this.shared){
2356
                                this.parents[parent] = this.parent;
2357
                        }
2358
                        return this.parent;
2359
                },
2360
                render: function(context, buffer){
2361
                        var parent = this.getParent(context);
2362
2363
                        parent.blocks = parent.blocks || {};
2364
                        buffer.blocks = buffer.blocks || {};
2365
2366
                        for(var i = 0, node; node = this.nodelist.contents[i]; i++){
2367
                                if(node instanceof dojox.dtl.tag.loader.BlockNode){
2368
                                        var old = parent.blocks[node.name];
2369
                                        if(old && old.nodelist != node.nodelist){
2370
                                                // In a shared template, the individual blocks might change
2371
                                                buffer = old.nodelist.unrender(context, buffer);
2372
                                        }
2373
                                        parent.blocks[node.name] = buffer.blocks[node.name] = {
2374
                                                shared: this.shared,
2375
                                                nodelist: node.nodelist,
2376
                                                used: false
2377
                                        }
2378
                                }
2379
                        }
2380
2381
                        this.rendered = parent;
2382
                        return parent.nodelist.render(context, buffer, this);
2383
                },
2384
                unrender: function(context, buffer){
2385
                        return this.rendered.unrender(context, buffer, this);
2386
                },
2387
                toString: function(){ return "dojox.dtl.block.ExtendsNode"; }
2388
        });
2389
2390
        ddtl.IncludeNode = dojo.extend(function(path, constant, getTemplate, text, parsed){
2391
                this._path = path;
2392
                this.constant = constant;
2393
                this.path = (constant) ? path : new dd._Filter(path);
2394
                this.getTemplate = getTemplate;
2395
                this.text = text;
2396
                this.parsed = (arguments.length == 5) ? parsed : true;
2397
        },
2398
        {
2399
                _cache: [{}, {}],
2400
                render: function(context, buffer){
2401
                        var location = ((this.constant) ? this.path : this.path.resolve(context)).toString();
2402
                        var parsed = Number(this.parsed);
2403
                        var dirty = false;
2404
                        if(location != this.last){
2405
                                dirty = true;
2406
                                if(this.last){
2407
                                        buffer = this.unrender(context, buffer);
2408
                                }
2409
                                this.last = location;
2410
                        }
2411
2412
                        var cache = this._cache[parsed];
2413
2414
                        if(parsed){
2415
                                if(!cache[location]){
2416
                                        cache[location] = dd.text._resolveTemplateArg(location, true);
2417
                                }
2418
                                if(dirty){
2419
                                        var template = this.getTemplate(cache[location]);
2420
                                        this.rendered = template.nodelist;
2421
                                }
2422
                                return this.rendered.render(context, buffer, this);
2423
                        }else{
2424
                                if(this.text instanceof dd._TextNode){
2425
                                        if(dirty){
2426
                                                this.rendered = this.text;
2427
                                                this.rendered.set(dd.text._resolveTemplateArg(location, true));
2428
                                        }
2429
                                        return this.rendered.render(context, buffer);
2430
                                }else{
2431
                                        if(!cache[location]){
2432
                                                var nodelist = [];
2433
                                                var div = document.createElement("div");
2434
                                                div.innerHTML = dd.text._resolveTemplateArg(location, true);
2435
                                                var children = div.childNodes;
2436
                                                while(children.length){
2437
                                                        var removed = div.removeChild(children[0]);
2438
                                                        nodelist.push(removed);
2439
                                                }
2440
                                                cache[location] = nodelist;
2441
                                        }
2442
                                        if(dirty){
2443
                                                this.nodelist = [];
2444
                                                var exists = true;
2445
                                                for(var i = 0, child; child = cache[location][i]; i++){
2446
                                                        this.nodelist.push(child.cloneNode(true));
2447
                                                }
2448
                                        }
2449
                                        for(var i = 0, node; node = this.nodelist[i]; i++){
2450
                                                buffer = buffer.concat(node);
2451
                                        }
2452
                                }
2453
                        }
2454
                        return buffer;
2455
                },
2456
                unrender: function(context, buffer){
2457
                        if(this.rendered){
2458
                                buffer = this.rendered.unrender(context, buffer);
2459
                        }
2460
                        if(this.nodelist){
2461
                                for(var i = 0, node; node = this.nodelist[i]; i++){
2462
                                        buffer = buffer.remove(node);
2463
                                }
2464
                        }
2465
                        return buffer;
2466
                },
2467
                clone: function(buffer){
2468
                        return new this.constructor(this._path, this.constant, this.getTemplate, this.text.clone(buffer), this.parsed);
2469
                }
2470
        });
2471
2472
        dojo.mixin(ddtl, {
2473
                block: function(parser, token){
2474
                        var parts = token.contents.split();
2475
                        var name = parts[1];
2476
2477
                        parser._blocks = parser._blocks || {};
2478
                        parser._blocks[name] = parser._blocks[name] || [];
2479
                        parser._blocks[name].push(name);
2480
2481
                        var nodelist = parser.parse(["endblock", "endblock " + name]).rtrim();
2482
                        parser.next_token();
2483
                        return new dojox.dtl.tag.loader.BlockNode(name, nodelist);
2484
                },
2485
                extends_: function(parser, token){
2486
                        var parts = token.contents.split();
2487
                        var shared = false;
2488
                        var parent = null;
2489
                        var key = null;
2490
                        if(parts[1].charAt(0) == '"' || parts[1].charAt(0) == "'"){
2491
                                parent = parts[1].substring(1, parts[1].length - 1);
2492
                        }else{
2493
                                key = parts[1];
2494
                        }
2495
                        if(parent && parent.indexOf("shared:") == 0){
2496
                                shared = true;
2497
                                parent = parent.substring(7, parent.length);
2498
                        }
2499
                        var nodelist = parser.parse();
2500
                        return new dojox.dtl.tag.loader.ExtendsNode(parser.getTemplate, nodelist, shared, parent, key);
2501
                },
2502
                include: function(parser, token){
2503
                        var parts = token.contents.split();
2504
                        if(parts.length != 2){
2505
                                throw new Error(parts[0] + " tag takes one argument: the name of the template to be included");
2506
                        }
2507
                        var path = parts[1];
2508
                        var constant = false;
2509
                        if((path.charAt(0) == '"' || path.slice(-1) == "'") && path.charAt(0) == path.slice(-1)){
2510
                                path = path.slice(1, -1);
2511
                                constant = true;
2512
                        }
2513
                        return new ddtl.IncludeNode(path, constant, parser.getTemplate, parser.create_text_node());
2514
                },
2515
                ssi: function(parser, token){
2516
                        // We're going to treat things a little differently here.
2517
                        // First of all, this tag is *not* portable, so I'm not
2518
                        // concerned about it being a "drop in" replacement.
2519
2520
                        // Instead, we'll just replicate the include tag, but with that
2521
                        // optional "parsed" parameter.
2522
                        var parts = token.contents.split();
2523
                        var parsed = false;
2524
                        if(parts.length == 3){
2525
                                parsed = (parts.pop() == "parsed");
2526
                                if(!parsed){
2527
                                        throw new Error("Second (optional) argument to ssi tag must be 'parsed'");
2528
                                }
2529
                        }
2530
                        var node = ddtl.include(parser, new dd.Token(token.token_type, parts.join(" ")));
2531
                        node.parsed = parsed;
2532
                        return node;
2533
                }
2534
        });
2535
})();
2536
2537
}
2538
2539
if(!dojo._hasResource["dojox.dtl.tag.misc"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2540
dojo._hasResource["dojox.dtl.tag.misc"] = true;
2541
dojo.provide("dojox.dtl.tag.misc");
2542
2543
2544
(function(){
2545
        var dd = dojox.dtl;
2546
        var ddtm = dd.tag.misc;
2547
2548
        ddtm.DebugNode = dojo.extend(function(text){
2549
                this.text = text;
2550
        },
2551
        {
2552
                render: function(context, buffer){
2553
                        var keys = context.getKeys();
2554
                        var debug = [];
2555
                        var only = {};
2556
                        for(var i = 0, key; key = keys[i]; i++){
2557
                                only[key] = context[key];
2558
                                debug += "[" + key + ": " + typeof context[key] + "]\n";
2559
                        }
2560
                        console.debug(only);
2561
                        return this.text.set(debug).render(context, buffer, this);
2562
                },
2563
                unrender: function(context, buffer){
2564
                        return buffer;
2565
                },
2566
                clone: function(buffer){
2567
                        return new this.constructor(this.text.clone(buffer));
2568
                },
2569
                toString: function(){ return "ddtm.DebugNode"; }
2570
        });
2571
2572
        ddtm.FilterNode = dojo.extend(function(varnode, nodelist){
2573
                this._varnode = varnode;
2574
                this._nodelist = nodelist;
2575
        },
2576
        {
2577
                render: function(context, buffer){
2578
                        // Doing this in HTML requires a different buffer with a fake root node
2579
                        var output = this._nodelist.render(context, new dojox.string.Builder());
2580
                        context = context.update({ "var": output.toString() });
2581
                        var filtered = this._varnode.render(context, buffer);
2582
                        context = context.pop();
2583
                        return buffer;
2584
                },
2585
                unrender: function(context, buffer){
2586
                        return buffer;
2587
                },
2588
                clone: function(buffer){
2589
                        return new this.constructor(this._expression, this._nodelist.clone(buffer));
2590
                }
2591
        });
2592
2593
        ddtm.FirstOfNode = dojo.extend(function(vars, text){
2594
                this._vars = vars;
2595
                this.vars = dojo.map(vars, function(item){
2596
                        return new dojox.dtl._Filter(item);
2597
                });
2598
                this.contents = text;
2599
        },
2600
        {
2601
                render: function(context, buffer){
2602
                        for(var i = 0, item; item = this.vars[i]; i++){
2603
                                var resolved = item.resolve(context);
2604
                                if(typeof resolved != "undefined"){
2605
                                        if(resolved === null){
2606
                                                resolved = "null";
2607
                                        }
2608
                                        this.contents.set(resolved);
2609
                                        return this.contents.render(context, buffer);
2610
                                }
2611
                        }
2612
                        return this.contents.unrender(context, buffer);
2613
                },
2614
                unrender: function(context, buffer){
2615
                        return this.contents.unrender(context, buffer);
2616
                },
2617
                clone: function(buffer){
2618
                        return new this.constructor(this._vars, this.contents.clone(buffer));
2619
                }
2620
        });
2621
2622
        ddtm.SpacelessNode = dojo.extend(function(nodelist, text){
2623
                this.nodelist = nodelist;
2624
                this.contents = text;
2625
        },
2626
        {
2627
                render: function(context, buffer){
2628
                        if(buffer.getParent){
2629
                                // Unfortunately, we have to branch here
2630
                                var watch = [
2631
                                        dojo.connect(buffer, "onAddNodeComplete", this, "_watch"),
2632
                                        dojo.connect(buffer, "onSetParent", this, "_watchParent")
2633
                                ];
2634
                                buffer = this.nodelist.render(context, buffer);
2635
                                dojo.disconnect(watch[0]);
2636
                                dojo.disconnect(watch[1]);
2637
                        }else{
2638
                                var value = this.nodelist.dummyRender(context);
2639
                                this.contents.set(value.replace(/>\s+</g, '><'));
2640
                                buffer = this.contents.render(context, buffer);
2641
                        }
2642
                        return buffer;
2643
                },
2644
                unrender: function(context, buffer){
2645
                        return this.nodelist.unrender(context, buffer);
2646
                },
2647
                clone: function(buffer){
2648
                        return new this.constructor(this.nodelist.clone(buffer), this.contents.clone(buffer));
2649
                },
2650
                _isEmpty: function(node){
2651
                        return (node.nodeType == 3 && !node.data.match(/[^\s\n]/));
2652
                },
2653
                _watch: function(node){
2654
                        if(this._isEmpty(node)){
2655
                                var remove = false;
2656
                                if(node.parentNode.firstChild == node){
2657
                                        node.parentNode.removeChild(node);
2658
                                }
2659
                        }else{
2660
                                var children = node.parentNode.childNodes;
2661
                                if(node.nodeType == 1 && children.length > 2){
2662
                                        for(var i = 2, child; child = children[i]; i++){
2663
                                                if(children[i - 2].nodeType == 1 && this._isEmpty(children[i - 1])){
2664
                                                        node.parentNode.removeChild(children[i - 1]);
2665
                                                        return;
2666
                                                }
2667
                                        }
2668
                                }
2669
                        }
2670
                },
2671
                _watchParent: function(node){
2672
                        var children = node.childNodes;
2673
                        if(children.length){
2674
                                while(node.childNodes.length){
2675
                                        var last = node.childNodes[node.childNodes.length - 1];
2676
                                        if(!this._isEmpty(last)){
2677
                                                return;
2678
                                        }
2679
                                        node.removeChild(last);
2680
                                }
2681
                        }
2682
                }
2683
        });
2684
2685
        ddtm.TemplateTagNode = dojo.extend(function(tag, text){
2686
                this.tag = tag;
2687
                this.contents = text;
2688
        },
2689
        {
2690
                mapping: {
2691
                        openblock: "{%",
2692
                        closeblock: "%}",
2693
                        openvariable: "{{",
2694
                        closevariable: "}}",
2695
                        openbrace: "{",
2696
                        closebrace: "}",
2697
                        opencomment: "{#",
2698
                        closecomment: "#}"
2699
                },
2700
                render: function(context, buffer){
2701
                        this.contents.set(this.mapping[this.tag]);
2702
                        return this.contents.render(context, buffer);
2703
                },
2704
                unrender: function(context, buffer){
2705
                        return this.contents.unrender(context, buffer);
2706
                },
2707
                clone: function(buffer){
2708
                        return new this.constructor(this.tag, this.contents.clone(buffer));
2709
                }
2710
        });
2711
2712
        ddtm.WidthRatioNode = dojo.extend(function(current, max, width, text){
2713
                this.current = new dd._Filter(current);
2714
                this.max = new dd._Filter(max);
2715
                this.width = width;
2716
                this.contents = text;
2717
        },
2718
        {
2719
                render: function(context, buffer){
2720
                        var current = +this.current.resolve(context);
2721
                        var max = +this.max.resolve(context);
2722
                        if(typeof current != "number" || typeof max != "number" || !max){
2723
                                this.contents.set("");
2724
                        }else{
2725
                                this.contents.set("" + Math.round((current / max) * this.width));
2726
                        }
2727
                        return this.contents.render(context, buffer);
2728
                },
2729
                unrender: function(context, buffer){
2730
                        return this.contents.unrender(context, buffer);
2731
                },
2732
                clone: function(buffer){
2733
                        return new this.constructor(this.current.getExpression(), this.max.getExpression(), this.width, this.contents.clone(buffer));
2734
                }
2735
        });
2736
2737
        ddtm.WithNode = dojo.extend(function(target, alias, nodelist){
2738
                this.target = new dd._Filter(target);
2739
                this.alias = alias;
2740
                this.nodelist = nodelist;
2741
        },
2742
        {
2743
                render: function(context, buffer){
2744
                        var target = this.target.resolve(context);
2745
                        context = context.push();
2746
                        context[this.alias] = target;
2747
                        buffer = this.nodelist.render(context, buffer);
2748
                        context = context.pop();
2749
                        return buffer;
2750
                },
2751
                unrender: function(context, buffer){
2752
                        return buffer;
2753
                },
2754
                clone: function(buffer){
2755
                        return new this.constructor(this.target.getExpression(), this.alias, this.nodelist.clone(buffer));
2756
                }
2757
        });
2758
2759
        dojo.mixin(ddtm, {
2760
                comment: function(parser, token){
2761
                        // summary: Ignore everything between {% comment %} and {% endcomment %}
2762
                        parser.skip_past("endcomment");
2763
                        return dd._noOpNode;
2764
                },
2765
                debug: function(parser, token){
2766
                        // summary: Output the current context, maybe add more stuff later.
2767
                        return new ddtm.DebugNode(parser.create_text_node());
2768
                },
2769
                filter: function(parser, token){
2770
                        // summary: Filter the contents of the blog through variable filters.
2771
                        var rest = token.contents.split(null, 1)[1];
2772
                        var varnode = parser.create_variable_node("var|" + rest);
2773
                        var nodelist = parser.parse(["endfilter"]);
2774
                        parser.next_token();
2775
                        return new ddtm.FilterNode(varnode, nodelist);
2776
                },
2777
                firstof: function(parser, token){
2778
                        var parts = token.split_contents().slice(1);
2779
                        if(!parts.length){
2780
                                throw new Error("'firstof' statement requires at least one argument");
2781
                        }
2782
                        return new ddtm.FirstOfNode(parts, parser.create_text_node());
2783
                },
2784
                spaceless: function(parser, token){
2785
                        var nodelist = parser.parse(["endspaceless"]);
2786
                        parser.delete_first_token();
2787
                        return new ddtm.SpacelessNode(nodelist, parser.create_text_node());
2788
                },
2789
                templatetag: function(parser, token){
2790
                        var parts = token.contents.split();
2791
                        if(parts.length != 2){
2792
                                throw new Error("'templatetag' statement takes one argument");
2793
                        }
2794
                        var tag = parts[1];
2795
                        var mapping = ddtm.TemplateTagNode.prototype.mapping;
2796
                        if(!mapping[tag]){
2797
                                var keys = [];
2798
                                for(var key in mapping){
2799
                                        keys.push(key);
2800
                                }
2801
                                throw new Error("Invalid templatetag argument: '" + tag + "'. Must be one of: " + keys.join(", "));
2802
                        }
2803
                        return new ddtm.TemplateTagNode(tag, parser.create_text_node());
2804
                },
2805
                widthratio: function(parser, token){
2806
                        var parts = token.contents.split();
2807
                        if(parts.length != 4){
2808
                                throw new Error("widthratio takes three arguments");
2809
                        }
2810
                        var width = +parts[3];
2811
                        if(typeof width != "number"){
2812
                                throw new Error("widthratio final argument must be an integer");
2813
                        }
2814
                        return new ddtm.WidthRatioNode(parts[1], parts[2], width, parser.create_text_node());
2815
                },
2816
                with_: function(parser, token){
2817
                        var parts = token.split_contents();
2818
                        if(parts.length != 4 || parts[2] != "as"){
2819
                                throw new Error("do_width expected format as 'with value as name'");
2820
                        }
2821
                        var nodelist = parser.parse(["endwith"]);
2822
                        parser.next_token();
2823
                        return new ddtm.WithNode(parts[1], parts[3], nodelist);
2824
                }
2825
        });
2826
})();
2827
2828
}
2829
2830
if(!dojo._hasResource["dojox.dtl.ext-dojo.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2831
dojo._hasResource["dojox.dtl.ext-dojo.NodeList"] = true;
2832
dojo.provide("dojox.dtl.ext-dojo.NodeList");
2833
2834
2835
dojo.extend(dojo.NodeList, {
2836
        dtl: function(template, context){
2837
                // template: dojox.dtl.__StringArgs|String
2838
                //                The template string or location
2839
                // context: dojox.dtl.__ObjectArgs|Object
2840
                //                The context object or location
2841
                var d = dojox.dtl;
2842
2843
                var self = this;
2844
                var render = function(template, context){
2845
                        var content = template.render(new d._Context(context));
2846
                        self.forEach(function(node){
2847
                                node.innerHTML = content;
2848
                        });
2849
                }
2850
2851
                d.text._resolveTemplateArg(template).addCallback(function(templateString){
2852
                        template = new d.Template(templateString);
2853
                        d.text._resolveContextArg(context).addCallback(function(context){
2854
                                render(template, context);
2855
                        });
2856
                });
2857
2858
                return this;
2859
        }
2860
});
2861
2862
}