_base.js 20 KB

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