/tags/release/7020-RC1/design/htmlmock/javascript/dojox/dtl/_base.js

http://aipo.googlecode.com/ · JavaScript · 632 lines · 527 code · 36 blank · 69 comment · 121 complexity · 9d9726441913c8010821c32cded5c235 MD5 · raw file

  1. if(!dojo._hasResource["dojox.dtl._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2. dojo._hasResource["dojox.dtl._base"] = true;
  3. dojo.provide("dojox.dtl._base");
  4. dojo.require("dojox.string.Builder");
  5. dojo.require("dojox.string.tokenize");
  6. dojox.dtl.Context = function(dict){
  7. dojo.mixin(this, dict || {});
  8. this._dicts = [];
  9. this._this = {};
  10. }
  11. dojo.extend(dojox.dtl.Context, {
  12. _dicts: [],
  13. _this: {},
  14. extend: function(/*dojox.dtl.Context|Object*/ obj){
  15. // summary: Returns a clone of this context object, with the items from the
  16. // passed objecct mixed in.
  17. var context = new dojox.dtl.Context();
  18. var keys = this.getKeys();
  19. for(var i = 0, key; key = keys[i]; i++){
  20. if(typeof obj[key] != "undefined"){
  21. context[key] = obj[key];
  22. }else{
  23. context[key] = this[key];
  24. }
  25. }
  26. if(obj instanceof dojox.dtl.Context){
  27. keys = obj.getKeys();
  28. }else if(typeof obj == "object"){
  29. keys = [];
  30. for(var key in obj){
  31. keys.push(key);
  32. }
  33. }
  34. for(var i = 0, key; key = keys[i]; i++){
  35. context[key] = obj[key];
  36. }
  37. return context;
  38. },
  39. filter: function(/*dojox.dtl.Context|Object|String...*/ filter){
  40. // summary: Returns a clone of this context, only containing the items
  41. // defined in the filter.
  42. var context = new dojox.dtl.Context();
  43. var keys = [];
  44. if(filter instanceof dojox.dtl.Context){
  45. keys = filter.getKeys();
  46. }else if(typeof filter == "object"){
  47. for(var key in filter){
  48. keys.push(key);
  49. }
  50. }else{
  51. for(var i = 0, arg; arg = arguments[i]; i++){
  52. if(typeof arg == "string"){
  53. keys.push(arg);
  54. }
  55. }
  56. }
  57. for(var i = 0, key; key = keys[i]; i++){
  58. context[key] = this[key];
  59. }
  60. return context;
  61. },
  62. setThis: function(/*Object*/ _this){
  63. this._this = _this;
  64. },
  65. getThis: function(){
  66. return this._this;
  67. },
  68. push: function(){
  69. var dict = {};
  70. var keys = this.getKeys();
  71. for(var i = 0, key; key = keys[i]; i++){
  72. dict[key] = this[key];
  73. delete this[key];
  74. }
  75. this._dicts.unshift(dict);
  76. },
  77. pop: function(){
  78. if(!this._dicts.length){
  79. throw new Error("pop() has been called more times than push() on the Context");
  80. }
  81. var dict = this._dicts.shift();
  82. dojo.mixin(this, dict);
  83. },
  84. hasKey: function(key){
  85. if(typeof this[key] != "undefined"){
  86. return true;
  87. }
  88. for(var i = 0, dict; dict = this._dicts[i]; i++){
  89. if(typeof dict[key] != "undefined"){
  90. return true;
  91. }
  92. }
  93. return false;
  94. },
  95. getKeys: function(){
  96. var keys = [];
  97. for(var key in this){
  98. if(isNaN(key)){
  99. var found = false;
  100. for(var protoKey in dojox.dtl.Context.prototype){
  101. if(key == protoKey){
  102. found = true;
  103. break;
  104. }
  105. }
  106. if(!found){
  107. keys.push(key);
  108. }
  109. }
  110. }
  111. return keys;
  112. },
  113. get: function(key, otherwise){
  114. if(typeof this[key] != "undefined"){
  115. return this[key];
  116. }
  117. for(var i = 0, dict; dict = this._dicts[i]; i++){
  118. if(typeof dict[key] != "undefined"){
  119. return dict[key];
  120. }
  121. }
  122. return otherwise;
  123. },
  124. update: function(dict){
  125. this.push();
  126. if(dict){
  127. dojo.mixin(this, dict);
  128. }
  129. },
  130. toString: function(){ return "dojox.dtl.Context"; }
  131. });
  132. dojox.dtl.text = {
  133. types: {tag: -1, varr: -2, text: 3},
  134. pySplit: function(str){
  135. // summary: Split a string according to Python's split function
  136. str = str.replace(/^\s+|\s+$/, "");
  137. if(!str.length){
  138. return [];
  139. }
  140. return str.split(/\s+/g);
  141. },
  142. urlquote: function(/*String*/ url, /*String?*/ safe){
  143. if(!safe){
  144. safe = "/";
  145. }
  146. return dojox.string.tokenize(url, /([^\w-_.])/g, function(token){
  147. if(safe.indexOf(token) == -1){
  148. if(token == " "){
  149. return "+";
  150. }else{
  151. return "%" + token.charCodeAt(0).toString(16).toUpperCase();
  152. }
  153. }
  154. return token;
  155. }).join("");
  156. },
  157. _get: function(module, name, errorless){
  158. // summary: Used to find both tags and filters
  159. var params = dojox.dtl.register.get(module, name, errorless);
  160. if(!params) return;
  161. var require = params.getRequire();
  162. var obj = params.getObj();
  163. var fn = params.getFn();
  164. if(fn.indexOf(":") != -1){
  165. var parts = fn.split(":");
  166. fn = parts.pop();
  167. }
  168. dojo.requireIf(true, require);
  169. var parent = window;
  170. var parts = obj.split(".");
  171. for(var i = 0, part; part = parts[i]; i++){
  172. if(!parent[part]) return;
  173. parent = parent[part];
  174. }
  175. return parent[fn || name] || parent[name + "_"];
  176. },
  177. getTag: function(name, errorless){
  178. return dojox.dtl.text._get("tag", name, errorless);
  179. },
  180. getFilter: function(name, errorless){
  181. return dojox.dtl.text._get("filter", name, errorless);
  182. },
  183. getTemplate: function(file){
  184. return new dojox.dtl.Template(dojox.dtl.getTemplateString(file));
  185. },
  186. getTemplateString: function(file){
  187. return dojo._getText(file.toString()) || "";
  188. },
  189. _re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(.+?)\s*%\})/g,
  190. tokenize: function(str){
  191. return dojox.string.tokenize(str, dojox.dtl.text._re, dojox.dtl.text._parseDelims);
  192. },
  193. _parseDelims: function(varr, tag){
  194. var types = dojox.dtl.text.types;
  195. if(varr){
  196. return [types.varr, varr];
  197. }else{
  198. return [types.tag, tag];
  199. }
  200. }
  201. }
  202. dojox.dtl.Template = function(str){
  203. var st = dojox.dtl;
  204. var tokens = st.text.tokenize(str);
  205. var parser = new st.Parser(tokens);
  206. this.nodelist = parser.parse();
  207. }
  208. dojo.extend(dojox.dtl.Template, {
  209. render: function(context, /*concatenatable?*/ buffer){
  210. context = context || new dojox.dtl.Context({});
  211. if(!buffer){
  212. dojo.require("dojox.string.Builder");
  213. buffer = new dojox.string.Builder();
  214. }
  215. return this.nodelist.render(context, buffer) + "";
  216. },
  217. toString: function(){ return "dojox.dtl.Template"; }
  218. });
  219. dojox.dtl.Filter = function(token){
  220. // summary: Uses a string to find (and manipulate) a variable
  221. if(!token) throw new Error("Filter must be called with variable name");
  222. this.contents = token;
  223. var key = null;
  224. var re = this._re;
  225. var matches, filter, arg, fn;
  226. var filters = [];
  227. while(matches = re.exec(token)){
  228. if(key === null){
  229. if(this._exists(matches, 3)){
  230. // variable
  231. key = matches[3];
  232. }else if(this._exists(matches, 1)){
  233. // _("text")
  234. key = '"' + matches[1] + '"';
  235. }else if(this._exists(matches, 2)){
  236. // "text"
  237. key = '"' + matches[2] + '"';
  238. }else if(this._exists(matches, 9)){
  239. // 'text'
  240. key = '"' + matches[9] + '"';
  241. }
  242. }else{
  243. if(this._exists(matches, 7)){
  244. // :variable
  245. arg = [true, matches[7]];
  246. }else if(this._exists(matches, 5)){
  247. // :_("text")
  248. arg = [false, dojox.dtl.replace(matches[5], '\\"', '"')];
  249. }else if(this._exists(matches, 6)){
  250. // :"text"
  251. arg = [false, dojox.dtl.replace(matches[6], '\\"', '"')];
  252. }else if(this._exists(matches, 8)){
  253. // :"text"
  254. arg = [false, dojox.dtl.replace(matches[8], "\\'", "'")];
  255. }
  256. // Get a named filter
  257. fn = dojox.dtl.text.getFilter(matches[4]);
  258. if(typeof fn != "function") throw new Error(matches[4] + " is not registered as a filter");
  259. filters.push([fn, arg]);
  260. }
  261. }
  262. this.key = key;
  263. this.filters = filters;
  264. }
  265. dojo.extend(dojox.dtl.Filter, {
  266. _re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
  267. _exists: function(arr, index){
  268. if(typeof arr[index] != "undefined" && arr[index] !== ""){
  269. return true;
  270. }
  271. return false;
  272. },
  273. resolve: function(context){
  274. var str = this.resolvePath(this.key, context);
  275. for(var i = 0, filter; filter = this.filters[i]; i++){
  276. // Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
  277. // and [1][1] is either the variable name of the string content.
  278. if(filter[1]){
  279. if(filter[1][0]){
  280. str = filter[0](str, this.resolvePath(filter[1][1], context));
  281. }else{
  282. str = filter[0](str, filter[1][1]);
  283. }
  284. }else{
  285. str = filter[0](str);
  286. }
  287. }
  288. return str;
  289. },
  290. resolvePath: function(path, context){
  291. var current, parts;
  292. var first = path.charAt(0);
  293. var last = path.charAt(path.length - 1);
  294. if(!isNaN(parseInt(first))){
  295. current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
  296. }else if(first == '"' && first == last){
  297. current = path.substring(1, path.length - 1);
  298. }else{;
  299. if(path == "true") return true;
  300. if(path == "false") return false;
  301. if(path == "null" || path == "None") return null;
  302. parts = path.split(".");
  303. current = context.get(parts.shift());
  304. while(parts.length){
  305. if(current && typeof current[parts[0]] != "undefined"){
  306. current = current[parts[0]];
  307. if(typeof current == "function"){
  308. if(current.alters_data){
  309. current = "";
  310. }else{
  311. current = current();
  312. }
  313. }
  314. }else{
  315. return "";
  316. }
  317. parts.shift();
  318. }
  319. }
  320. return current;
  321. },
  322. toString: function(){ return "dojox.dtl.Filter"; }
  323. });
  324. dojox.dtl.Node = function(/*Object*/ obj){
  325. // summary: Basic catch-all node
  326. this.contents = obj;
  327. }
  328. dojo.extend(dojox.dtl.Node, {
  329. render: function(context, buffer){
  330. // summary: Adds content onto the buffer
  331. return buffer.concat(this.contents);
  332. },
  333. toString: function(){ return "dojox.dtl.Node"; }
  334. });
  335. dojox.dtl.NodeList = function(/*Node[]*/ nodes){
  336. // summary: Allows us to render a group of nodes
  337. this.contents = nodes || [];
  338. }
  339. dojo.extend(dojox.dtl.NodeList, {
  340. push: function(node){
  341. // summary: Add a new node to the list
  342. this.contents.push(node);
  343. },
  344. render: function(context, buffer){
  345. // summary: Adds all content onto the buffer
  346. for(var i = 0; i < this.contents.length; i++){
  347. buffer = this.contents[i].render(context, buffer);
  348. if(!buffer) throw new Error("Template node render functions must return their buffer");
  349. }
  350. return buffer;
  351. },
  352. unrender: function(context, buffer){ return buffer; },
  353. clone: function(){ return this; },
  354. toString: function(){ return "dojox.dtl.NodeList"; }
  355. });
  356. dojox.dtl.TextNode = dojox.dtl.Node;
  357. dojox.dtl.VarNode = function(str){
  358. // summary: A node to be processed as a variable
  359. this.contents = new dojox.dtl.Filter(str);
  360. }
  361. dojo.extend(dojox.dtl.VarNode, {
  362. render: function(context, buffer){
  363. var str = this.contents.resolve(context);
  364. return buffer.concat(str);
  365. },
  366. toString: function(){ return "dojox.dtl.VarNode"; }
  367. });
  368. dojox.dtl.Parser = function(tokens){
  369. // summary: Parser used during initialization and for tag groups.
  370. this.contents = tokens;
  371. }
  372. dojo.extend(dojox.dtl.Parser, {
  373. parse: function(/*Array?*/ stop_at){
  374. // summary: Turns tokens into nodes
  375. // description: Steps into tags are they're found. Blocks use the parse object
  376. // to find their closing tag (the stop_at array). stop_at is inclusive, it
  377. // returns the node that matched.
  378. var st = dojox.dtl;
  379. var types = st.text.types;
  380. var terminators = {};
  381. var tokens = this.contents;
  382. stop_at = stop_at || [];
  383. for(var i = 0; i < stop_at.length; i++){
  384. terminators[stop_at[i]] = true;
  385. }
  386. var nodelist = new st.NodeList();
  387. while(tokens.length){
  388. token = tokens.shift();
  389. if(typeof token == "string"){
  390. nodelist.push(new st.TextNode(token));
  391. }else{
  392. var type = token[0];
  393. var text = token[1];
  394. if(type == types.varr){
  395. nodelist.push(new st.VarNode(text));
  396. }else if(type == types.tag){
  397. if(terminators[text]){
  398. tokens.unshift(token);
  399. return nodelist;
  400. }
  401. var cmd = text.split(/\s+/g);
  402. if(cmd.length){
  403. cmd = cmd[0];
  404. var fn = dojox.dtl.text.getTag(cmd);
  405. if(fn){
  406. nodelist.push(fn(this, text));
  407. }
  408. }
  409. }
  410. }
  411. }
  412. if(stop_at.length){
  413. throw new Error("Could not find closing tag(s): " + stop_at.toString());
  414. }
  415. return nodelist;
  416. },
  417. next: function(){
  418. // summary: Returns the next token in the list.
  419. var token = this.contents.shift();
  420. return {type: token[0], text: token[1]};
  421. },
  422. skipPast: function(endtag){
  423. var types = dojox.dtl.text.types;
  424. while(this.contents.length){
  425. var token = this.contents.shift();
  426. if(token[0] == types.tag && token[1] == endtag){
  427. return;
  428. }
  429. }
  430. throw new Error("Unclosed tag found when looking for " + endtag);
  431. },
  432. getVarNode: function(){
  433. return dojox.dtl.VarNode;
  434. },
  435. getTextNode: function(){
  436. return dojox.dtl.TextNode;
  437. },
  438. getTemplate: function(file){
  439. return new dojox.dtl.Template(file);
  440. },
  441. toString: function(){ return "dojox.dtl.Parser"; }
  442. });
  443. dojox.dtl.register = function(module, cols, args, /*Function*/ normalize){
  444. // summary: Used to create dojox.dtl.register[module] function, and as a namespace
  445. // expand: Used if the call structure is reformatted for a more compact view.
  446. // Should return an array of normalized arguments.
  447. // description: The function produced will accept a "name"
  448. // as the first parameter and all other parameters will
  449. // be associated with the parameter names listed in cols.
  450. var ddr = dojox.dtl.register;
  451. var registry = ddr._mod[module] = {
  452. params: [],
  453. Getter: function(params){
  454. ddr._params = params || {};
  455. }
  456. };
  457. cols.unshift("name");
  458. for(var i = 0, col; col = cols[i]; i++){
  459. registry.Getter.prototype["get" + col.substring(0, 1).toUpperCase() + col.substring(1, col.length)] = ddr._ret(i);
  460. }
  461. ddr[module] = function(/*String*/ name, /*mixed...*/ parameters){
  462. if(normalize){
  463. var normalized = normalize(arguments);
  464. }else{
  465. var normalized = [arguments];
  466. }
  467. for(var i = 0, args; args = normalized[i]; i++){
  468. var params = [];
  469. for(var j = 0; j < cols.length; j++){
  470. params.push(args[j] || null);
  471. }
  472. if(typeof args[0] == "string"){
  473. // Strings before regexes for speed
  474. registry.params.unshift(params);
  475. }else{
  476. // break
  477. // module RegExp
  478. registry.params.push(params);
  479. }
  480. }
  481. }
  482. ddr[module].apply(null, args);
  483. }
  484. dojo.mixin(dojox.dtl.register, {
  485. _mod: {},
  486. _ret: function(i){
  487. // summary: Just lets use i and _params within a closure
  488. return function(){
  489. return dojox.dtl.register._params[i] || "";
  490. }
  491. },
  492. get: function(/*String*/ module, /*String*/ name, /*Boolean*/ errorless){
  493. // summary: Returns a "Getter", based on the registry
  494. // description: The getter functions correspond with the registered cols
  495. // used in dojo.register
  496. var registry = this._mod[module] || {};
  497. if(registry.params){
  498. for(var i = 0, param; param = registry.params[i]; i++){
  499. var search = param[0];
  500. if(typeof search == "string"){
  501. if(search == name){
  502. return new registry.Getter(param);
  503. }
  504. }else if(name.match(search)){
  505. var matches = search.exec(name);
  506. var mixin = [];
  507. dojo.mixin(mixin, param);
  508. mixin[0] = matches[1];
  509. return new registry.Getter(param);
  510. }
  511. }
  512. }
  513. if(!errorless) throw new Error("'" + module + "' of name '" + name + "' does not exist");
  514. },
  515. _normalize: function(args){
  516. // summary:
  517. // Translates to the signature (/*String*/ name, /*String*/ require, /*String*/ obj, /*String*/ fn)
  518. var items = args[2];
  519. var output = [];
  520. for(var i = 0, item; item = items[i]; i++){
  521. if(typeof item == "string"){
  522. output.push([item, args[0], args[1], item]);
  523. }else{
  524. output.push([item[0], args[0], args[1], item[1]]);
  525. }
  526. }
  527. return output;
  528. },
  529. tag: function(/*String*/ require, /*String*/ obj, /*String[]|[RegExp, String][]*/ fns){
  530. // summary:
  531. // Specify the location of a given tag function.
  532. // require:
  533. // The file this function is in
  534. // obj:
  535. // The base object to use for lookups
  536. // fn:
  537. // List of functions within obj to use
  538. // description:
  539. // When we are looking up a tag as specified in a template, we either use a
  540. // string in the fns array, or the RegExp item of the [RegExp, String] pair.
  541. // When that string is found, it requires the file specified in the require
  542. // parameter, uses the base object as a starting point and checks for obj.fn
  543. // or obj.fn_ in case fn is a reserved word.
  544. this("tag", ["require", "obj", "fn"], arguments, this._normalize);
  545. },
  546. filter: function(/*String*/ require, /*String*/ obj, /*String[]|[RegExp, String][]*/ fns){
  547. // summary:
  548. // Specify the location of a given filter function.
  549. // require:
  550. // The file this function is in
  551. // obj:
  552. // The base object to use for lookups
  553. // fn:
  554. // List of functions within obj to use
  555. // description:
  556. // When we are looking up a tag as specified in a template, we either use a
  557. // string in the fns array, or the RegExp item of the [RegExp, String] pair.
  558. // When that string is found, it requires the file specified in the require
  559. // parameter, uses the base object as a starting point and checks for obj.fn
  560. // or obj.fn_ in case fn is a reserved word.
  561. this("filter", ["require", "obj", "fn"], arguments, this._normalize);
  562. }
  563. });
  564. (function(){
  565. var register = dojox.dtl.register;
  566. var dtt = "dojox.dtl.tag";
  567. register.tag(dtt + ".logic", dtt + ".logic", ["if", "for"]);
  568. register.tag(dtt + ".loader", dtt + ".loader", ["extends", "block"]);
  569. register.tag(dtt + ".misc", dtt + ".misc", ["comment", "debug", "filter"]);
  570. register.tag(dtt + ".loop", dtt + ".loop", ["cycle"]);
  571. var dtf = "dojox.dtl.filter";
  572. register.filter(dtf + ".dates", dtf + ".dates", ["date", "time", "timesince", "timeuntil"]);
  573. register.filter(dtf + ".htmlstrings", dtf + ".htmlstrings", ["escape", "linebreaks", "linebreaksbr", "removetags", "striptags"]);
  574. register.filter(dtf + ".integers", dtf + ".integers", ["add", "get_digit"]);
  575. register.filter(dtf + ".lists", dtf + ".lists", ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"]);
  576. register.filter(dtf + ".logic", dtf + ".logic", ["default", "default_if_none", "divisibleby", "yesno"]);
  577. register.filter(dtf + ".misc", dtf + ".misc", ["filesizeformat", "pluralize", "phone2numeric", "pprint"]);
  578. register.filter(dtf + ".strings", dtf + ".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"]);
  579. })();
  580. dojox.dtl.replace = function(str, token, repl){
  581. repl = repl || "";
  582. var pos, len = token.length;
  583. while(1){
  584. pos = str.indexOf(token);
  585. if(pos == -1) break;
  586. str = str.substring(0, pos) + repl + str.substring(pos + len);
  587. }
  588. return str;
  589. }
  590. dojox.dtl.resolveVariable = function(token, context){
  591. // summary: Quickly resolve a variables
  592. var filter = new dojox.dtl.Filter(token);
  593. return filter.resolve(context);
  594. }
  595. }