(function(){ var rsplit = function(string, regex) { var result = regex.exec(string),retArr = new Array(), first_idx, last_idx, first_bit; while (result != null) { first_idx = result.index; last_idx = regex.lastIndex; if ((first_idx) != 0) { first_bit = string.substring(0,first_idx); retArr.push(string.substring(0,first_idx)); string = string.slice(first_idx); } retArr.push(result[0]); string = string.slice(result[0].length); result = regex.exec(string); } if (! string == '') { retArr.push(string); } return retArr; }, chop = function(string){ return string.substr(0, string.length - 1); }, extend = function(d, s){ for(var n in s){ if(s.hasOwnProperty(n)) d[n] = s[n] } } EJS = function( options ){ options = typeof options == "string" ? {view: options} : options this.set_options(options); if(options.precompiled){ this.template = {}; this.template.process = options.precompiled; EJS.update(this.name, this); return; } if(options.element) { if(typeof options.element == 'string'){ var name = options.element options.element = document.getElementById( options.element ) if(options.element == null) throw name+'does not exist!' } if(options.element.value){ this.text = options.element.value }else{ this.text = options.element.innerHTML } this.name = options.element.id this.type = '[' }else if(options.url){ options.url = EJS.endExt(options.url, this.extMatch); this.name = this.name ? this.name : options.url; var url = options.url //options.view = options.absolute_url || options.view || options.; var template = EJS.get(this.name /*url*/, this.cache); if (template) return template; if (template == EJS.INVALID_PATH) return null; try{ this.text = EJS.request( url+(this.cache ? '' : '?'+Math.random() )); }catch(e){} if(this.text == null){ throw( {type: 'EJS', message: 'There is no template at '+url} ); } //this.name = url; } var template = new EJS.Compiler(this.text, this.type); template.compile(options, this.name); EJS.update(this.name, this); this.template = template; }; /* @Prototype*/ EJS.prototype = { /** * Renders an object with extra view helpers attached to the view. * @param {Object} object data to be rendered * @param {Object} extra_helpers an object with additonal view helpers * @return {String} returns the result of the string */ render : function(object, extra_helpers){ object = object || {}; this._extra_helpers = extra_helpers; var v = new EJS.Helpers(object, extra_helpers || {}); return this.template.process.call(object, object,v); }, update : function(element, options){ if(typeof element == 'string'){ element = document.getElementById(element) } if(options == null){ _template = this; return function(object){ EJS.prototype.update.call(_template, element, object) } } if(typeof options == 'string'){ params = {} params.url = options _template = this; params.onComplete = function(request){ var object = eval( request.responseText ) EJS.prototype.update.call(_template, element, object) } EJS.ajax_request(params) }else { element.innerHTML = this.render(options) } }, out : function(){ return this.template.out; }, /** * Sets options on this view to be rendered with. * @param {Object} options */ set_options : function(options){ this.type = options.type || EJS.type; this.cache = options.cache != null ? options.cache : EJS.cache; this.text = options.text || null; this.name = options.name || null; this.ext = options.ext || EJS.ext; this.extMatch = new RegExp(this.ext.replace(/\./, '\.')); } }; EJS.endExt = function(path, match){ if(!path) return null; match.lastIndex = 0 return path+ (match.test(path) ? '' : this.ext ) } /* @Static*/ EJS.Scanner = function(source, left, right) { extend(this, {left_delimiter: left +'%', right_delimiter: '%'+right, double_left: left+'%%', double_right: '%%'+right, left_equal: left+'%=', left_comment: left+'%#'}) this.SplitRegexp = left=='[' ? /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/ : 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)') ; this.source = source; this.stag = null; this.lines = 0; }; EJS.Scanner.to_text = function(input){ if(input == null || input === undefined) return ''; if(input instanceof Date) return input.toDateString(); if(input.toString) return input.toString(); return ''; }; EJS.Scanner.prototype = { scan: function(block) { scanline = this.scanline; regex = this.SplitRegexp; if (! this.source == '') { var source_split = rsplit(this.source, /\n/); for(var i=0; i 0) { for (var i=0; i 0) { buff.push(put_cmd + '"' + clean(content) + '")'); } content = ''; break; case scanner.double_left: content = content + scanner.left_delimiter; break; default: content = content + token; break; } } else { switch(token) { case scanner.right_delimiter: switch(scanner.stag) { case scanner.left_delimiter: if (content[content.length - 1] == '\n') { content = chop(content); buff.push(content); buff.cr(); } else { buff.push(content); } break; case scanner.left_equal: buff.push(insert_cmd + "(EJS.Scanner.to_text(" + content + ")))"); break; } scanner.stag = null; content = ''; break; case scanner.double_right: content = content + scanner.right_delimiter; break; default: content = content + token; break; } } }); if (content.length > 0) { // Chould be content.dump in Ruby buff.push(put_cmd + '"' + clean(content) + '")'); } buff.close(); this.out = buff.script + ";"; var to_be_evaled = '/*'+name+'*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};"; try{ eval(to_be_evaled); }catch(e){ if(typeof JSLINT != 'undefined'){ JSLINT(this.out); for(var i = 0; i < JSLINT.errors.length; i++){ var error = JSLINT.errors[i]; if(error.reason != "Unnecessary semicolon."){ error.line++; var e = new Error(); e.lineNumber = error.line; e.message = error.reason; if(options.view) e.fileName = options.view; throw e; } } }else{ throw e; } } } }; //type, cache, folder /** * Sets default options for all views * @param {Object} options Set view with the following options *
OptionDefaultDescription
type '<' type of magic tags. Options are '<' or '['
cache true in production mode, false in other modes true to cache template.
* */ EJS.config = function(options){ EJS.cache = options.cache != null ? options.cache : EJS.cache; EJS.type = options.type != null ? options.type : EJS.type; EJS.ext = options.ext != null ? options.ext : EJS.ext; var templates_directory = EJS.templates_directory || {}; //nice and private container EJS.templates_directory = templates_directory; EJS.get = function(path, cache){ if(cache == false) return null; if(templates_directory[path]) return templates_directory[path]; return null; }; EJS.update = function(path, template) { if(path == null) return; templates_directory[path] = template ; }; EJS.INVALID_PATH = -1; }; EJS.config( {cache: true, type: '<', ext: '.ejs' } ); /** * @constructor * By adding functions to EJS.Helpers.prototype, those functions will be available in the * views. * @init Creates a view helper. This function is called internally. You should never call it. * @param {Object} data The data passed to the view. Helpers have access to it through this._data */ EJS.Helpers = function(data, extras){ this._data = data; this._extras = extras; extend(this, extras ); }; /* @prototype*/ EJS.Helpers.prototype = { /** * Renders a new view. If data is passed in, uses that to render the view. * @param {Object} options standard options passed to a new view. * @param {optional:Object} data * @return {String} */ view: function(options, data, helpers){ if(!helpers) helpers = this._extras if(!data) data = this._data; return new EJS(options).render(data, helpers); }, /** * For a given value, tries to create a human representation. * @param {Object} input the value being converted. * @param {Object} null_text what text should be present if input == null or undefined, defaults to '' * @return {String} */ to_text: function(input, null_text) { if(input == null || input === undefined) return null_text || ''; if(input instanceof Date) return input.toDateString(); if(input.toString) return input.toString().replace(/\n/g, '
').replace(/''/g, "'"); return ''; } }; EJS.newRequest = function(){ var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }]; for(var i = 0; i < factories.length; i++) { try { var request = factories[i](); if (request != null) return request; } catch(e) { continue;} } } EJS.request = function(path){ var request = new EJS.newRequest() request.open("GET", path, false); try{request.send(null);} catch(e){return null;} if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null; return request.responseText } EJS.ajax_request = function(params){ params.method = ( params.method ? params.method : 'GET') var request = new EJS.newRequest(); request.onreadystatechange = function(){ if(request.readyState == 4){ if(request.status == 200){ params.onComplete(request) }else { params.onComplete(request) } } } request.open(params.method, params.url) request.send(null) } })();