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