PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/drillbit/modules/drillbit/0.1.0/lib/ejs.js

https://github.com/bataboske/titanium_mobile
JavaScript | 470 lines | 416 code | 15 blank | 39 comment | 32 complexity | 1137bc22c5216175365647121b28babb MD5 | raw file
  1. /*--------------------------------------------------------------------------
  2. * EJS - Embedded JavaScript, version 0.1.0
  3. * Copyright (c) 2007 Edward Benson
  4. * http://www.edwardbenson.com/projects/ejs
  5. * ------------------------------------------------------------------------
  6. *
  7. * EJS is freely distributable under the terms of an MIT-style license.
  8. *
  9. * EJS is a client-side preprocessing engine written in and for JavaScript.
  10. * If you have used PHP, ASP, JSP, or ERB then you get the idea: code embedded
  11. * in <% // Code here %> tags will be executed, and code embedded in <%= .. %>
  12. * tags will be evaluated and appended to the output.
  13. *
  14. * This is essentially a direct JavaScript port of Masatoshi Seki's erb.rb
  15. * from the Ruby Core, though it contains a subset of ERB's functionality.
  16. *
  17. * Requirements:
  18. * prototype.js
  19. *
  20. * Usage:
  21. * // source should be either a string or a DOM node whose innerHTML
  22. * // contains EJB source.
  23. * var source = "<% var ejb="EJB"; %><h1>Hello, <%= ejb %>!</h1>";
  24. * var compiler = new EjsCompiler(source);
  25. * compiler.compile();
  26. * var output = eval(compiler.out);
  27. * alert(output); // -> "<h1>Hello, EJB!</h1>"
  28. *
  29. * For a demo: see demo.html
  30. * For the license: see license.txt
  31. *
  32. *--------------------------------------------------------------------------*/
  33. /* Make a split function like Ruby's: "abc".split(/b/) -> ['a', 'b', 'c'] */
  34. String.prototype.rsplit = function(regex) {
  35. var item = this;
  36. var result = regex.exec(item);
  37. var retArr = new Array();
  38. while (result != null)
  39. {
  40. var first_idx = result.index;
  41. var last_idx = regex.lastIndex;
  42. if ((first_idx) != 0)
  43. {
  44. var first_bit = item.substring(0,first_idx);
  45. retArr.push(item.substring(0,first_idx));
  46. item = item.slice(first_idx);
  47. }
  48. retArr.push(result[0]);
  49. item = item.slice(result[0].length);
  50. result = regex.exec(item);
  51. }
  52. if (! item == '')
  53. {
  54. retArr.push(item);
  55. }
  56. return retArr;
  57. };
  58. /* Chop is nice to have too */
  59. String.prototype.chop = function() {
  60. return this.substr(0, this.length - 1);
  61. }
  62. /* Adaptation from the Scanner of erb.rb */
  63. var EjsScanner = function(source, left, right) {
  64. this.left_delimiter = left +'%' //<%
  65. this.right_delimiter = '%'+right //>
  66. this.double_left = left+'%%'
  67. this.double_right = '%%'+right
  68. this.left_equal = left+'%='
  69. this.left_comment = left+'%#'
  70. if(left=='[')
  71. this.SplitRegexp = /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/;
  72. else
  73. this.SplitRegexp = new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)')
  74. this.source = source;
  75. this.stag = null;
  76. this.lines = 0;
  77. };
  78. EjsView = function(data){
  79. this.data = data
  80. }
  81. EjsView.prototype.partial = function(options, data){
  82. if(!data) data = this.data;
  83. return new EJS(options).render(data);
  84. }
  85. EjsScanner.to_text = function(input){
  86. if(input == null || input === undefined)
  87. return '';
  88. if(input instanceof Date)
  89. return input.toDateString();
  90. if(input.toString)
  91. return input.toString()
  92. return '';
  93. }
  94. EjsScanner.prototype = {
  95. /* For each line, scan! */
  96. scan: function(block) {
  97. scanline = this.scanline;
  98. regex = this.SplitRegexp;
  99. if (! this.source == '')
  100. {
  101. var source_split = this.source.rsplit(/\n/);
  102. for(var i=0; i<source_split.length; i++) {
  103. var item = source_split[i];
  104. this.scanline(item, regex, block);
  105. }
  106. }
  107. },
  108. /* For each token, block! */
  109. scanline: function(line, regex, block) {
  110. this.lines++
  111. var line_split = line.rsplit(regex);
  112. for(var i=0; i<line_split.length; i++) {
  113. var token = line_split[i];
  114. if (token != null) {
  115. try{
  116. block(token, this);
  117. }catch(e){
  118. throw {type: 'EjsScanner', line: this.lines}
  119. }
  120. }
  121. }
  122. }
  123. };
  124. /* Adaptation from the Buffer of erb.rb */
  125. var EjsBuffer = function(pre_cmd, post_cmd) {
  126. this.line = new Array();
  127. this.script = "";
  128. this.pre_cmd = pre_cmd;
  129. this.post_cmd = post_cmd;
  130. for (var i=0; i<this.pre_cmd.length; i++)
  131. {
  132. this.push(pre_cmd[i]);
  133. }
  134. }
  135. EjsBuffer.prototype = {
  136. push: function(cmd) {
  137. this.line.push(cmd);
  138. },
  139. cr: function() {
  140. this.script = this.script + this.line.join('; ');
  141. this.line = new Array();
  142. this.script = this.script + "\n";
  143. },
  144. close: function() {
  145. if (this.line.length > 0)
  146. {
  147. for (var i=0; i<this.post_cmd.length; i++)
  148. {
  149. this.push(pre_cmd[i]);
  150. }
  151. this.script = this.script + this.line.join('; ');
  152. line = null;
  153. }
  154. }
  155. };
  156. /* Adaptation from the Compiler of erb.rb */
  157. EjsCompiler = function(source, left) {
  158. this.pre_cmd = ['___ejsO = "";'];
  159. this.post_cmd = new Array();
  160. this.source = ' ';
  161. if (source != null)
  162. {
  163. if (typeof source == 'string')
  164. {
  165. source = source.replace(/\r\n/g, "\n");
  166. source = source.replace(/\r/g, "\n");
  167. this.source = source;
  168. }
  169. else if (source.innerHTML)
  170. {
  171. this.source = source.innerHTML;
  172. }
  173. if (typeof this.source != 'string')
  174. {
  175. this.source = "";
  176. }
  177. }
  178. left = left || '<'
  179. var right = '>'
  180. switch(left) {
  181. case '[':
  182. right = ']'
  183. break;
  184. case '<':
  185. break;
  186. default:
  187. throw left+' is not a supported deliminator'
  188. break;
  189. }
  190. this.scanner = new EjsScanner(this.source, left, right);
  191. this.out = '';
  192. }
  193. EjsCompiler.prototype = {
  194. compile: function(options) {
  195. options = options || {};
  196. this.out = '';
  197. var put_cmd = "___ejsO += ";
  198. var insert_cmd = put_cmd;
  199. var buff = new EjsBuffer(this.pre_cmd, this.post_cmd);
  200. var content = '';
  201. var clean = function(content)
  202. {
  203. content = content.replace(/\\/g, '\\\\');
  204. content = content.replace(/\n/g, '\\n');
  205. content = content.replace(/"/g, '\\"');
  206. return content;
  207. }
  208. this.scanner.scan(function(token, scanner) {
  209. if (scanner.stag == null)
  210. {
  211. //alert(token+'|'+(token == "\n"))
  212. switch(token) {
  213. case '\n':
  214. content = content + "\n";
  215. buff.push(put_cmd + '"' + clean(content) + '";');
  216. buff.cr()
  217. content = '';
  218. break;
  219. case scanner.left_delimiter:
  220. case scanner.left_equal:
  221. case scanner.left_comment:
  222. scanner.stag = token;
  223. if (content.length > 0)
  224. {
  225. // Chould be content.dump in Ruby
  226. buff.push(put_cmd + '"' + clean(content) + '"');
  227. }
  228. content = '';
  229. break;
  230. case scanner.double_left:
  231. content = content + scanner.left_delimiter;
  232. break;
  233. default:
  234. content = content + token;
  235. break;
  236. }
  237. }
  238. else {
  239. switch(token) {
  240. case scanner.right_delimiter:
  241. switch(scanner.stag) {
  242. case scanner.left_delimiter:
  243. if (content[content.length - 1] == '\n')
  244. {
  245. content = content.chop();
  246. buff.push(content);
  247. buff.cr();
  248. }
  249. else {
  250. buff.push(content);
  251. }
  252. break;
  253. case scanner.left_equal:
  254. buff.push(insert_cmd + "(EjsScanner.to_text(" + content + "))");
  255. break;
  256. }
  257. scanner.stag = null;
  258. content = '';
  259. break;
  260. case scanner.double_right:
  261. content = content + scanner.right_delimiter;
  262. break;
  263. default:
  264. content = content + token;
  265. break;
  266. }
  267. }
  268. });
  269. if (content.length > 0)
  270. {
  271. // Chould be content.dump in Ruby
  272. buff.push(put_cmd + '"' + clean(content) + '"');
  273. }
  274. buff.close();
  275. this.out = buff.script + ";";
  276. var to_be_evaled = 'this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ejsO;}}}catch(e){e.lineNumber=null;throw e;}};";
  277. try{
  278. eval(to_be_evaled);
  279. }catch(e){
  280. if(typeof JSLINT != 'undefined'){
  281. JSLINT(this.out)
  282. for(var i = 0; i < JSLINT.errors.length; i++){
  283. var error = JSLINT.errors[i];
  284. if(error.reason != "Unnecessary semicolon."){
  285. error.line++;
  286. var e = new Error();
  287. e.lineNumber = error.line;
  288. e.message = error.reason;
  289. if(options.url)
  290. e.fileName = options.url;
  291. throw e;
  292. }
  293. }
  294. }else{
  295. throw e;
  296. }
  297. }
  298. }
  299. }
  300. //type, cache, folder
  301. EJS = function( options ){
  302. this.set_options(options)
  303. if(options.url){
  304. var template = EJS.get(options.url, this.cache)
  305. if (template) return template;
  306. if (template == EJS.INVALID_PATH) return null;
  307. this.text = EJS.request(options.url)
  308. if(this.text == null){
  309. //EJS.update(options.url, this.INVALID_PATH);
  310. throw 'There is no template at '+options.url
  311. }
  312. this.name = options.url
  313. }
  314. else if (options.text)
  315. {
  316. this.text = options.text;
  317. this.name = options.name;
  318. }
  319. else if(options.element)
  320. {
  321. if(typeof options.element == 'string'){
  322. var name = options.element
  323. options.element = document.getElementById( options.element )
  324. if(options.element == null) throw name+'does not exist!'
  325. }
  326. if(options.element.value){
  327. this.text = options.element.value
  328. }else{
  329. this.text = options.element.innerHTML
  330. }
  331. this.name = options.element.id
  332. this.type = '['
  333. }
  334. var template = new EjsCompiler(this.text, this.type);
  335. template.compile(options);
  336. EJS.update(this.name, this);
  337. this.template = template
  338. }
  339. EJS.config = function(options){
  340. EJS.cache = options.cache != null ? options.cache : EJS.cache
  341. EJS.type = options.type != null ? options.type : EJS.type
  342. var templates_directory = {} //nice and private container
  343. EJS.get = function(path, cache){
  344. if(cache == false) return null;
  345. if(templates_directory[path]) return templates_directory[path];
  346. return null;
  347. }
  348. EJS.update = function(path, template) {
  349. if(path == null) return;
  350. templates_directory[path] = template
  351. }
  352. EJS.INVALID_PATH = -1;
  353. }
  354. EJS.config( {cache: true, type: '<' } )
  355. EJS.prototype = {
  356. render : function(object){
  357. var v = new EjsView(object);
  358. return this.template.process.call(v, object,v);
  359. },
  360. out : function(){
  361. return this.template.out
  362. },
  363. set_options : function(options){
  364. this.type = options.type != null ? options.type : EJS.type
  365. this.cache = options.cache != null ? options.cache : EJS.cache
  366. this.text = options.text != null ? options.text : null
  367. this.name = options.name != null ? options.name : null
  368. },
  369. // called without options, returns a function that takes the object
  370. // called with options being a string, uses that as a url
  371. // called with options as an object
  372. update : function(element, options){
  373. if(typeof element == 'string'){
  374. element = document.getElementById(element)
  375. }
  376. if(options == null){
  377. _template = this;
  378. return function(object){
  379. EJS.prototype.update.call(_template, element, object)
  380. }
  381. }
  382. if(typeof options == 'string'){
  383. params = {}
  384. params.url = options
  385. _template = this;
  386. params.onComplete = function(request){
  387. var object = eval( request.responseText )
  388. EJS.prototype.update.call(_template, element, object)
  389. }
  390. EJS.ajax_request(params)
  391. }else
  392. {
  393. element.innerHTML = this.render(options)
  394. }
  395. }
  396. }
  397. EJS.newRequest = function(){
  398. var factories = [function() { return Titanium.Network.createHTTPClient(); }, function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];
  399. for(var i = 0; i < factories.length; i++) {
  400. try {
  401. var request = factories[i]();
  402. if (request != null) return request;
  403. }
  404. catch(e) { continue;}
  405. }
  406. }
  407. EJS.request = function(path){
  408. var request = new EJS.newRequest()
  409. request.open("GET", path, false);
  410. try{request.send(null);}
  411. catch(e){return null;}
  412. if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null;
  413. return request.responseText
  414. }
  415. EJS.ajax_request = function(params){
  416. params.method = ( params.method ? params.method : 'GET')
  417. var request = new EJS.newRequest();
  418. request.onreadystatechange = function(){
  419. if(request.readyState == 4){
  420. if(request.status == 200){
  421. params.onComplete(request)
  422. }else
  423. {
  424. params.onComplete(request)
  425. }
  426. }
  427. }
  428. request.open(params.method, params.url)
  429. request.send(null)
  430. }
  431. //}
  432. Titanium.createEJS = function(options) {
  433. return new EJS(options);
  434. }