@pgaubatz/npm-registry-couchapp
Version:
The CouchApp implementation of an npm registry
701 lines (642 loc) • 23.3 kB
JavaScript
(function($) {
// Embeddedjs is property of http://embeddedjs.com/
// Sammy ejs plugin written Codeofficer @ http://www.codeofficer.com/
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;
};
var chop = function(string) {
return string.substr(0, string.length - 1);
};
var extend = function(d, s) {
for (var n in s) {
if(s.hasOwnProperty(n)) d[n] = s[n];
};
};
/* @Constructor*/
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<source_split.length; i++) {
var item = source_split[i];
this.scanline(item, regex, block);
};
};
},
scanline: function(line, regex, block) {
this.lines++;
var line_split = rsplit(line, regex);
for (var i=0; i<line_split.length; i++) {
var token = line_split[i];
if (token != null) {
try {
block(token, this);
} catch(e) {
throw {type: 'EJS.Scanner', line: this.lines};
};
};
};
}
};
EJS.Buffer = function(pre_cmd, post_cmd) {
this.line = new Array();
this.script = "";
this.pre_cmd = pre_cmd;
this.post_cmd = post_cmd;
for (var i=0; i<this.pre_cmd.length; i++) {
this.push(pre_cmd[i]);
};
};
EJS.Buffer.prototype = {
push: function(cmd) {
this.line.push(cmd);
},
cr: function() {
this.script = this.script + this.line.join('; ');
this.line = new Array();
this.script = this.script + "\n";
},
close: function() {
if (this.line.length > 0) {
for (var i=0; i<this.post_cmd.length; i++){
this.push(pre_cmd[i]);
};
this.script = this.script + this.line.join('; ');
line = null;
};
}
};
EJS.Compiler = function(source, left) {
this.pre_cmd = ['var ___ViewO = [];'];
this.post_cmd = new Array();
this.source = ' ';
if (source != null) {
if (typeof(source) == 'string') {
source = source.replace(/\r\n/g, "\n");
source = source.replace(/\r/g, "\n");
this.source = source;
} else if (source.innerHTML) {
this.source = source.innerHTML;
};
if (typeof this.source != 'string') {
this.source = "";
};
};
left = left || '<';
var right = '>';
switch(left) {
case '[':
right = ']';
break;
case '<':
break;
default:
throw left+' is not a supported deliminator';
break;
};
this.scanner = new EJS.Scanner(this.source, left, right);
this.out = '';
};
EJS.Compiler.prototype = {
compile: function(options, name) {
options = options || {};
this.out = '';
var put_cmd = "___ViewO.push(";
var insert_cmd = put_cmd;
var buff = new EJS.Buffer(this.pre_cmd, this.post_cmd);
var content = '';
var clean = function(content) {
content = content.replace(/\\/g, '\\\\');
content = content.replace(/\n/g, '\\n');
content = content.replace(/"/g, '\\"');
return content;
};
this.scanner.scan(function(token, scanner) {
if (scanner.stag == null) {
switch(token) {
case '\n':
content = content + "\n";
buff.push(put_cmd + '"' + clean(content) + '");');
buff.cr();
content = '';
break;
case scanner.left_delimiter:
case scanner.left_equal:
case scanner.left_comment:
scanner.stag = token;
if (content.length > 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
* <table class="options">
<tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr>
<tr>
<td>type</td>
<td>'<'</td>
<td>type of magic tags. Options are '<' or '['
</td>
</tr>
<tr>
<td>cache</td>
<td>true in production mode, false in other modes</td>
<td>true to cache template.
</td>
</tr>
</tbody></table>
*
*/
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, '<br />').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);
};
EJS.Helpers.prototype.date_tag = function(name, value , html_options) {
if(!(value instanceof(Date))) value = new Date();
var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var years = [], months = [], days =[];
var year = value.getFullYear();
var month = value.getMonth();
var day = value.getDate();
for (var y = year - 15; y < year+15 ; y++) {
years.push({value: y, text: y});
};
for (var m = 0; m < 12; m++) {
months.push({value: (m), text: month_names[m]});
};
for (var d = 0; d < 31; d++) {
days.push({value: (d+1), text: (d+1)});
};
var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} );
var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'});
var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'});
return year_select+month_select+day_select;
};
EJS.Helpers.prototype.form_tag = function(action, html_options) {
html_options = html_options || {};
html_options.action = action;
if(html_options.multipart == true) {
html_options.method = 'post';
html_options.enctype = 'multipart/form-data';
};
return this.start_tag_for('form', html_options);
};
EJS.Helpers.prototype.form_tag_end = function() {
return this.tag_end('form');
};
EJS.Helpers.prototype.hidden_field_tag = function(name, value, html_options) {
return this.input_field_tag(name, value, 'hidden', html_options);
};
EJS.Helpers.prototype.input_field_tag = function(name, value , inputType, html_options) {
html_options = html_options || {};
html_options.id = html_options.id || name;
html_options.value = value || '';
html_options.type = inputType || 'text';
html_options.name = name;
return this.single_tag_for('input', html_options);
};
EJS.Helpers.prototype.is_current_page = function(url) {
return (window.location.href == url || window.location.pathname == url ? true : false);
};
EJS.Helpers.prototype.link_to = function(name, url, html_options) {
if(!name) var name = 'null';
if(!html_options) var html_options = {};
if(html_options.confirm){
html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} ";
html_options.confirm = null;
};
html_options.href = url;
return this.start_tag_for('a', html_options)+name+ this.tag_end('a');
};
EJS.Helpers.prototype.submit_link_to = function(name, url, html_options) {
if(!name) var name = 'null';
if(!html_options) var html_options = {};
html_options.onclick = html_options.onclick || '';
if(html_options.confirm){
html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} ";
html_options.confirm = null;
};
html_options.value = name;
html_options.type = 'submit';
html_options.onclick = html_options.onclick + (url ? this.url_for(url) : '')+'return false;';
return this.start_tag_for('input', html_options);
};
EJS.Helpers.prototype.link_to_if = function(condition, name, url, html_options, post, block) {
return this.link_to_unless((condition == false), name, url, html_options, post, block);
};
EJS.Helpers.prototype.link_to_unless = function(condition, name, url, html_options, block) {
html_options = html_options || {};
if(condition) {
if(block && typeof(block) == 'function') {
return block(name, url, html_options, block);
} else {
return name;
};
} else {
return this.link_to(name, url, html_options);
};
};
EJS.Helpers.prototype.link_to_unless_current = function(name, url, html_options, block) {
html_options = html_options || {};
return this.link_to_unless(this.is_current_page(url), name, url, html_options, block);
};
EJS.Helpers.prototype.password_field_tag = function(name, value, html_options) {
return this.input_field_tag(name, value, 'password', html_options);
};
EJS.Helpers.prototype.select_tag = function(name, value, choices, html_options) {
html_options = html_options || {};
html_options.id = html_options.id || name;
html_options.value = value;
html_options.name = name;
var txt = '';
txt += this.start_tag_for('select', html_options);
for(var i = 0; i < choices.length; i++) {
var choice = choices[i];
var optionOptions = {value: choice.value};
if(choice.value == value) optionOptions.selected ='selected';
txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option');
};
txt += this.tag_end('select');
return txt;
};
EJS.Helpers.prototype.single_tag_for = function(tag, html_options) {
return this.tag(tag, html_options, '/>');
};
EJS.Helpers.prototype.start_tag_for = function(tag, html_options) {
return this.tag(tag, html_options);
};
EJS.Helpers.prototype.submit_tag = function(name, html_options) {
html_options = html_options || {};
html_options.type = html_options.type || 'submit';
html_options.value = name || 'Submit';
return this.single_tag_for('input', html_options);
};
EJS.Helpers.prototype.tag = function(tag, html_options, end) {
if(!end) var end = '>';
var txt = ' ';
for(var attr in html_options) {
if(html_options[attr] != null) {
var value = html_options[attr].toString();
} else {
var value='';
};
// special case because "class" is a reserved word in IE
if(attr == "Class") attr = "class";
if( value.indexOf("'") != -1 ) {
txt += attr+'=\"'+value+'\" ';
} else {
txt += attr+"='"+value+"' ";
};
};
return '<' + tag + txt + end;
};
EJS.Helpers.prototype.tag_end = function(tag) {
return '</'+tag+'>';
};
EJS.Helpers.prototype.text_area_tag = function(name, value, html_options) {
html_options = html_options || {};
html_options.id = html_options.id || name;
html_options.name = html_options.name || name;
value = value || '';
if(html_options.size) {
html_options.cols = html_options.size.split('x')[0];
html_options.rows = html_options.size.split('x')[1];
delete html_options.size;
};
html_options.cols = html_options.cols || 50;
html_options.rows = html_options.rows || 4;
return this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea');
};
EJS.Helpers.prototype.text_tag = EJS.Helpers.prototype.text_area_tag;
EJS.Helpers.prototype.text_field_tag = function(name, value, html_options) {
return this.input_field_tag(name, value, 'text', html_options);
};
EJS.Helpers.prototype.url_for = function(url) {
return 'window.location="' + url + '";';
};
EJS.Helpers.prototype.img_tag = function(image_location, alt, options){
options = options || {};
options.src = image_location;
options.alt = alt;
return this.single_tag_for('img', options);
};
// -------------------------------------------------------------
Sammy = Sammy || {};
Sammy.EJS = function(app, method_alias) {
// *Helper:* Uses simple templating to parse ERB like templates.
//
// ### Arguments
//
// * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data.
// * `data` An Object containing the replacement values for the template.
//data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
// * `name` An optional String name to cache the template.
//
var template = function(template, data, name) {
// use name for caching
if (typeof name == 'undefined') name = template;
return new EJS({text: template, name: name}).render(data);
};
// set the default method name/extension
if (!method_alias) method_alias = 'ejs';
// create the helper at the method alias
app.helper(method_alias, template);
};
})(jQuery);