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