Repository: aefxx/jQote2 Branch: master Commit: 474253a3f737 Files: 20 Total size: 175.1 KB Directory structure: gitextract_dddq6nf1/ ├── MIT-LICENSE.txt ├── README ├── WTFPL-LICENSE.txt ├── external/ │ ├── ejs_production.js │ ├── jquery.benchmark.js │ ├── jquery.mustache.js │ ├── jquery.nano.js │ ├── jquery.srender.js │ ├── jquery.tempest.js │ ├── jquery.templates.js │ ├── jquery.tmpl.js │ ├── pure.js │ ├── qunit.css │ ├── qunit.js │ ├── styles.css │ └── underscore.js ├── jqote.benchmark.htm ├── jqote.qunit.htm ├── jquery.jqote2.js └── version.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: MIT-LICENSE.txt ================================================ Copyright (c) 2010 aefxx, http://aefxx.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README ================================================ ================================================ FILE: WTFPL-LICENSE.txt ================================================ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar 14 rue de Plaisance, 75014 Paris, France Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. ================================================ FILE: external/ejs_production.js ================================================ (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;var template=EJS.get(this.name,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})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={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},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)};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;i0){for(var i=0;i0){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){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").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")};EJS.Helpers.prototype.start_tag_for=function(A,B){return this.tag(A,B)};EJS.Helpers.prototype.submit_tag=function(A,B){B=B||{};B.type=B.type||"submit";B.value=A||"Submit";return this.single_tag_for("input",B)};EJS.Helpers.prototype.tag=function(C,E,D){if(!D){var D=">"}var B=" ";for(var A in E){if(E[A]!=null){var F=E[A].toString()}else{var F=""}if(A=="Class"){A="class"}if(F.indexOf("'")!=-1){B+=A+'="'+F+'" '}else{B+=A+"='"+F+"' "}}return"<"+C+B+D};EJS.Helpers.prototype.tag_end=function(A){return""};EJS.Helpers.prototype.text_area_tag=function(A,C,B){B=B||{};B.id=B.id||A;B.name=B.name||A;C=C||"";if(B.size){B.cols=B.size.split("x")[0];B.rows=B.size.split("x")[1];delete B.size}B.cols=B.cols||50;B.rows=B.rows||4;return this.start_tag_for("textarea",B)+C+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(A,C,B){return this.input_field_tag(A,C,"text",B)};EJS.Helpers.prototype.url_for=function(A){return'window.location="'+A+'";'};EJS.Helpers.prototype.img_tag=function(B,C,A){A=A||{};A.src=B;A.alt=C;return this.single_tag_for("img",A)} ================================================ FILE: external/jquery.benchmark.js ================================================ // based on methodology developed by PPK: // http://www.quirksmode.org/blog/archives/2009/08/when_to_read_ou.html (function($){ $.benchmark = function(n, contestant, test){ var startTime = new Date().getTime(); while (n--) contestant.benchmarks[test].call(contestant.templates); setTimeout(function () { var endTime = new Date().getTime(); var result = (endTime-startTime)/1000; contestant.results.push(result); },10); }; })(jQuery); ================================================ FILE: external/jquery.mustache.js ================================================ /* Shameless port of a shameless port @defunkt => @janl => @aq See http://github.com/defunkt/mustache for more info. */ ;(function($) { /* mustache.js — Logic-less templates in JavaScript See http://mustache.github.com/ for more info. */ var Mustache = function() { var Renderer = function() {}; Renderer.prototype = { otag: "{{", ctag: "}}", pragmas: {}, buffer: [], pragmas_implemented: { "IMPLICIT-ITERATOR": true }, context: {}, render: function(template, context, partials, in_recursion) { // reset buffer & set context if(!in_recursion) { this.context = context; this.buffer = []; // TODO: make this non-lazy } // fail fast if(!this.includes("", template)) { if(in_recursion) { return template; } else { this.send(template); return; } } template = this.render_pragmas(template); var html = this.render_section(template, context, partials); if(in_recursion) { return this.render_tags(html, context, partials, in_recursion); } this.render_tags(html, context, partials, in_recursion); }, /* Sends parsed lines */ send: function(line) { if(line != "") { this.buffer.push(line); } }, /* Looks for %PRAGMAS */ render_pragmas: function(template) { // no pragmas if(!this.includes("%", template)) { return template; } var that = this; var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + this.ctag); return template.replace(regex, function(match, pragma, options) { if(!that.pragmas_implemented[pragma]) { throw({message: "This implementation of mustache doesn't understand the '" + pragma + "' pragma"}); } that.pragmas[pragma] = {}; if(options) { var opts = options.split("="); that.pragmas[pragma][opts[0]] = opts[1]; } return ""; // ignore unknown pragmas silently }); }, /* Tries to find a partial in the curent scope and render it */ render_partial: function(name, context, partials) { name = this.trim(name); if(!partials || partials[name] === undefined) { throw({message: "unknown_partial '" + name + "'"}); } if(typeof(context[name]) != "object") { return this.render(partials[name], context, partials, true); } return this.render(partials[name], context[name], partials, true); }, /* Renders inverted (^) and normal (#) sections */ render_section: function(template, context, partials) { if(!this.includes("#", template) && !this.includes("^", template)) { return template; } var that = this; // CSW - Added "+?" so it finds the tighest bound, not the widest var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + "\\s*", "mg"); // for each {{#foo}}{{/foo}} section do... return template.replace(regex, function(match, type, name, content) { var value = that.find(name, context); if(type == "^") { // inverted section if(!value || that.is_array(value) && value.length === 0) { // false or empty list, render it return that.render(content, context, partials, true); } else { return ""; } } else if(type == "#") { // normal section if(that.is_array(value)) { // Enumerable, Let's loop! return that.map(value, function(row) { return that.render(content, that.create_context(row), partials, true); }).join(""); } else if(that.is_object(value)) { // Object, Use it as subcontext! return that.render(content, that.create_context(value), partials, true); } else if(typeof value === "function") { // higher order section return value.call(context, content, function(text) { return that.render(text, context, partials, true); }); } else if(value) { // boolean section return that.render(content, context, partials, true); } else { return ""; } } }); }, /* Replace {{foo}} and friends with values from our view */ render_tags: function(template, context, partials, in_recursion) { // tit for tat var that = this; var new_regex = function() { return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + that.ctag + "+", "g"); }; var regex = new_regex(); var tag_replace_callback = function(match, operator, name) { switch(operator) { case "!": // ignore comments return ""; case "=": // set new delimiters, rebuild the replace regexp that.set_delimiters(name); regex = new_regex(); return ""; case ">": // render partial return that.render_partial(name, context, partials); case "{": // the triple mustache is unescaped return that.find(name, context); default: // escape the value return that.escape(that.find(name, context)); } }; var lines = template.split("\n"); for(var i = 0; i < lines.length; i++) { lines[i] = lines[i].replace(regex, tag_replace_callback, this); if(!in_recursion) { this.send(lines[i]); } } if(in_recursion) { return lines.join("\n"); } }, set_delimiters: function(delimiters) { var dels = delimiters.split(" "); this.otag = this.escape_regex(dels[0]); this.ctag = this.escape_regex(dels[1]); }, escape_regex: function(text) { // thank you Simon Willison if(!arguments.callee.sRE) { var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ]; arguments.callee.sRE = new RegExp( '(\\' + specials.join('|\\') + ')', 'g' ); } return text.replace(arguments.callee.sRE, '\\$1'); }, /* find `name` in current `context`. That is find me a value from the view object */ find: function(name, context) { name = this.trim(name); // Checks whether a value is thruthy or false or 0 function is_kinda_truthy(bool) { return bool === false || bool === 0 || bool; } var value; if(is_kinda_truthy(context[name])) { value = context[name]; } else if(is_kinda_truthy(this.context[name])) { value = this.context[name]; } if(typeof value === "function") { return value.apply(context); } if(value !== undefined) { return value; } // silently ignore unkown variables return ""; }, // Utility methods /* includes tag */ includes: function(needle, haystack) { return haystack.indexOf(this.otag + needle) != -1; }, /* Does away with nasty characters */ escape: function(s) { s = String(s === null ? "" : s); return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) { switch(s) { case "&": return "&"; case "\\": return "\\\\"; case '"': return '\"'; case "<": return "<"; case ">": return ">"; default: return s; } }); }, // by @langalex, support for arrays of strings create_context: function(_context) { if(this.is_object(_context)) { return _context; } else { var iterator = "."; if(this.pragmas["IMPLICIT-ITERATOR"]) { iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; } var ctx = {}; ctx[iterator] = _context; return ctx; } }, is_object: function(a) { return a && typeof a == "object"; }, is_array: function(a) { return Object.prototype.toString.call(a) === '[object Array]'; }, /* Gets rid of leading and trailing whitespace */ trim: function(s) { return s.replace(/^\s*|\s*$/g, ""); }, /* Why, why, why? Because IE. Cry, cry cry. */ map: function(array, fn) { if (typeof array.map == "function") { return array.map(fn); } else { var r = []; var l = array.length; for(var i = 0; i < l; i++) { r.push(fn(array[i])); } return r; } } }; return({ name: "mustache.js", version: "0.3.1-dev", /* Turns a template and view into HTML */ to_html: function(template, view, partials, send_fun) { var renderer = new Renderer(); if(send_fun) { renderer.send = send_fun; } renderer.render(template, view, partials); if(!send_fun) { return renderer.buffer.join("\n"); } } }); }(); $.mustache = Mustache.to_html; })(jQuery); ================================================ FILE: external/jquery.nano.js ================================================ /* Nano Templates (Tomasz Mazur, Jacek Becela) */ (function($){ $.nano = function(template, data) { return template.replace(/\{([\w\.]*)\}/g, function (str, key) { var keys = key.split("."), value = data[keys.shift()]; $.each(keys, function () { value = value[this]; }); return (value === null || value === undefined) ? "" : value; }); }; })(jQuery); ================================================ FILE: external/jquery.srender.js ================================================ // Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed // adapted from: http://ejohn.org/blog/javascript-micro-templating/ // by Greg Borenstein http://ideasfordozens.com in Feb 2009 jQuery.srender = function(template, data, target){ jQuery.srender.cache = {}; // target is an optional element; if provided, the result will be inserted into it // otherwise the result will simply be returned to the caller if(jQuery.srender.cache[template]){ fn = jQuery.srender.cache[template]; } else{ // Generate a reusable function that will serve as a template // generator (and which will be cached). fn = jQuery.srender.cache[template] = new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){} "with(obj){p.push('" + // Convert the template into pure JavaScript template .replace(/[\r\t\n]/g, " ") .split("<%").join("\t") .replace(/((^|%>)[^\t]*)'/g, "$1\r") .replace(/\t=(.*?)%>/g, "',$1,'") .split("\t").join("');") .split("%>").join("p.push('") .split("\r").join("\\'") + "');}return p.join('');"); } // populate the optional element // or return the result if(target){ target.html(fn(data)); return false; } else{ return fn(data); } }; ================================================ FILE: external/jquery.tempest.js ================================================ // Tempest jQuery Templating Plugin // ================================ // // Copyright (c) 2009, 2010 Nick Fitzgerald - http://fitzgeraldnick.com/ // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // JSLint "use strict"; (function ($) { // PRIVATE VARIABLES var templateCache = {}, // TAG REGULAR EXPRESSIONS // Overwrite these if you want, but don't blame me when stuff goes wrong. OPEN_VAR_TAG = /\{\{[\s]*?/g, CLOSE_VAR_TAG = /[\s]*?\}\}/g, OPEN_BLOCK_TAG = /\{%[\s]*?/g, CLOSE_BLOCK_TAG = /[\s]*?%\}/g, // Probably, you don't want to mess with these, as they are built from // the ones above. VAR_TAG = new RegExp(OPEN_VAR_TAG.source + "[\\w\\-\\.]+?" + CLOSE_VAR_TAG.source, "g"), BLOCK_TAG = new RegExp(OPEN_BLOCK_TAG.source + "[\\w]+?(?:[ ]+?[\\w\\-\\.]*?)*?" + CLOSE_BLOCK_TAG.source, "g"), END_BLOCK_TAG = new RegExp(OPEN_BLOCK_TAG.source + "end[\\w]*?" + CLOSE_BLOCK_TAG.source, "g"), // All block tags stored in here. Tags have a couple things to work // with: // // * "args" property is set before render: // - Example: {% tag_type arg1 arg2 foo bar %} // * The "args" property would be set to // ["arg1", "arg2", "foo", "bar"] // in this example. The tag's render method could look them // up in the context object, or could do whatever it wanted // to do with it. // * "subNodes" property which is an array of all the nodes between // the block tag and it's corresponding {% end... %} tag // - NOTE: This property is only set for a block if it has the // "expectsEndTag" property set to true. // * Every block tag should have a "render" method that takes one // argument: a context object. It should return a string. BLOCK_NODES = { "for": { expectsEndTag: true, render: function renderFor(context) { var args = this.args, subNodes = this.subNodes, subNodesLen = subNodes.length, renderedNodes = [], i, itemName, arrName, arr, arrLength, forContext, tmpObj; if (args.length === 3 && args[1] === "in") { itemName = args[0]; arrName = args[2]; arr = getContextValue(arrName, context); arrLength = arr.length; for (i = 0; i < arrLength; i++) { tmpObj = {}; tmpObj[itemName] = arr[i]; tmpObj._index = i; forContext = $.extend({}, context, tmpObj); for (j = 0; j < subNodesLen; j++) { renderedNodes.push(subNodes[j].render(forContext)); } } return renderedNodes.join(""); } else { throw new TemplateSyntaxError( "Bad for tag syntax. Use {% for in %}" ); } } }, "if": { expectsEndTag: true, render: function renderIf(context) { var rendered_nodes = [], subNodes = this.subNodes; // Check the truthiness of the argument. if (!!getContextValue(this.args[0], context)) { $.each(subNodes, function (i, node) { rendered_nodes.push(node.render(context)); }); } return rendered_nodes.join(""); } } }, // Base text node object for prototyping. baseTextNode = { render: function (context) { return this.text || ""; } }, // Base variable node object for prototyping. baseVarNode = { render: function (context) { var val = context[this.name] === undefined ? "" : context[this.name]; if (val === "" && this.name.search(/\./) !== -1) { return getContextValue(this.name, context); } return cleanVal(val); } }; // CUSTOM ERRORS function TemplateSyntaxError(message) { if (!(this instanceof TemplateSyntaxError)) { return new TemplateSyntaxError(message); } this.message = message; } TemplateSyntaxError.prototype = new SyntaxError(); TemplateSyntaxError.prototype.name = "TemplateSyntaxError"; // PRIVATE FUNCTIONS // Some browsers don't return the grouped part of the RegExp with the array, // so we must accomodate them. var split = (function () { if ("abc".split(/(b)/).length === 3) { return function split(str, delimiter) { return String.prototype .split .call(str, delimiter); }; } else { return function split(str, delimiter) { if (Object.prototype .toString .call(delimiter) === "[object RegExp]") { var regex = delimiter.ignoreCase ? new RegExp(delimiter.source, "gi") : new RegExp(delimiter.source, "g"), match, match_str = "", arr = [], i, len = str.length; for (i = 0; i < len; i++) { match_str += str.charAt(i); match = match_str.match(regex); if (match !== null && match.length > 0) { arr.push(match_str.replace(match[0], "")); arr.push(match[0]); match_str = ""; } } if (match_str !== "") { arr.push(match_str); } return arr; } else { return String.prototype .split .call(str, delimiter); } }; } }()); function isBlockTag(token) { return token.search(BLOCK_TAG) !== -1; } function isEndTag(token) { return token.search(END_BLOCK_TAG) !== -1; } function isVarTag(token) { return token.search(VAR_TAG) !== -1; } function strip(str) { return str.replace(/^[\s]+/, "").replace(/[\s]+$/, ""); } // Clean the passed value the best we can. function cleanVal(val) { if (val instanceof $) { return jQueryToString(val); } else if (val !== null && !isArray(val) && typeof(val) === "object") { if (typeof(val.toHTML) === "function") { return cleanVal(val.toHTML()); } else { return val.toString(); } } else { return val; } } // Traverse a path of a context object from a string representation, // for example "object.child.attr". function getContextValue(str, context) { var path = split(str, "."), val = context[path[0]], i; for (i = 1; i < path.length; i++) { // Return an empty string if the lookup ever hits undefined. if (val !== undefined) { val = val[path[i]]; } else { return ""; } } // Make sure the last piece did not end up undefined. val = val === undefined ? "" : val; return cleanVal(val); } // Hack to get the HTML of a jquery object as a string. function jQueryToString(jq) { return $(document.createElement("div")).append(jq).html(); } // Make a new copy of a given object. function makeObj(obj) { if (obj === undefined) { return obj; } var O = function () {}; O.prototype = obj; return new O(); } // Return an array of key/template pairs. function storedTemplates() { var cache = []; $.each(templateCache, function (key, templ) { cache.push([ key, templ ]); }); return cache; } // Determine if the string is a key to a stored template or a // one-time-use template. function chooseTemplate(str) { return typeof templateCache[str] === "string" ? templateCache[str] : str; } // Return true if (and only if) an object is an array. function isArray(objToTest) { return Object.prototype .toString .apply(objToTest) === "[object Array]"; } // Call a rendering function on arrays of objects or just a single // object seamlessly. function renderEach(data, f) { return isArray(data) ? $.each(data, f) : f(0, data); } // Split a template in to tokens which will eventually be converted to // nodes and then rendered. function tokenize(templ) { return (function (arr) { var tokens = []; for (i = 0; i < arr.length; i++) { (function (token) { return token === "" ? null : tokens.push(token); }(arr[i])); } return tokens; }(split(templ, new RegExp("(" + VAR_TAG.source + "|" + BLOCK_TAG.source + "|" + END_BLOCK_TAG.source + ")")))); } // "Lisp in C's clothing." - Douglas Crockford function cdr(arr) { return arr.slice(1); } // Array.push changes the original array in place and returns the new // length of the array rather than the the actual array itself. This // makes it unchainable, which is ridiculous. function append(item, list) { return list.concat([item]); } // Take a token and create a variable node from it. function makeVarNode(token) { var node = makeObj(baseVarNode); node.name = strip(token.replace(OPEN_VAR_TAG, "") .replace(CLOSE_VAR_TAG, "")); return node; } // Take a token and create a text node from it. function makeTextNode(token) { var node = makeObj(baseTextNode); node.text = token; return node; } // A recursive function that terminates either when all tokens have // been converted to nodes or an end-block tag is found. function makeNodes(tokens) { return (function (nodes, tokens) { var token = tokens[0]; return tokens.length === 0 ? [nodes, [], true] : isEndTag(token) ? [nodes, cdr(tokens)] : isVarTag(token) ? arguments.callee(append(makeVarNode(token), nodes), cdr(tokens)) : isBlockTag(token) ? makeBlockNode(nodes, tokens, arguments.callee) : // Else assume it is a text node. arguments.callee(append(makeTextNode(token), nodes), cdr(tokens)); }([], tokens)); } // Split a block tags contents in to an array of bits that contains the // type of block node, and any arguments that were passed to the block // node if they exist. function makeBits(blockToken) { return (function (bits, split) { // Remove empty strings and strip whitespace. for (i = 0; i < split.length; i++) { (function (bit) { return bit === "" ? null : bits.push(bit); }(strip(split[i]))); } return bits; }([], split(blockToken.replace(OPEN_BLOCK_TAG, "") .replace(CLOSE_BLOCK_TAG, ""), /[\s]+?/))); } // Create a block tag's node by hijacking the "makeNodes" function // until an end-block is found. function makeBlockNode(nodes, tokens, f) { // Remove the templating syntax and split the type of block tag and // its arguments. var bits = makeBits(tokens[0]), // The type of block tag is the first of the bits, the rest // (if present) are args type = bits[0], args = cdr(bits), // Make the node from the set of block tags that Tempest knows // about. node = makeObj(BLOCK_NODES[type]), resultsArray; // Ensure that the type of block tag is one that is defined in // BLOCK_NODES if (node === undefined) { throw new TemplateSyntaxError("Unknown Block Tag."); } node.args = args; tokens = cdr(tokens); if (node.expectsEndTag === true) { resultsArray = makeNodes(tokens); if (resultsArray[2] !== undefined) { // The third item in the array returned by makeNodes is // only defined if the last of the tokens was made in to a // node and it wasn't an end-block tag. throw new TemplateSyntaxError( "A block tag was expecting an ending tag but it was not found." ); } node.subNodes = resultsArray[0]; tokens = resultsArray[1]; } // Add the newly created node to the nodes list. nodes = append(node, nodes); // Continue where we were before the block node. return f(nodes, tokens); } // Return the template rendered with the given object(s) as a jQuery // object. function renderToJQ(str, objects) { var template = chooseTemplate(str), lines = []; renderEach(objects, function (i, obj) { var resultsArray = makeNodes(tokenize(template), obj), nodes = resultsArray[0]; // Check for tokens left over in the results array, this means // that not all tokens were rendered because there are more // end-block tagss than block tags that expect an end. if (resultsArray[1].length !== 0) { throw new TemplateSyntaxError( "An unexpected end tag was found." ); } // Render each node and push it to the lines. $.each(nodes, function accumulateRendered(i, node) { lines.push(node.render(obj)); }); }); // Return the joined templates as jQuery objects if it appears to start // with an HTML tag, otherwise just return the string itself. return (function (str) { return str.charAt(0) === "<" ? $(str) : str; }(strip(lines.join("")))); } // EXTEND JQUERY OBJECT $.extend({ tempest: function () { var args = arguments; if (args.length === 0) { // Return key/template pairs of all stored templates. return storedTemplates(); } else if (args.length === 2 && typeof(args[0]) === "string" && typeof(args[1]) === "object") { // Render the supplied template (args[0], template name of // existing or one-time-use template) with the context data // (args[1]). return renderToJQ(args[0], args[1]); } else if (args.length === 1 && typeof(args[0]) === "string") { // Template getter. return templateCache[args[0]]; } else if (args.length === 2 && typeof(args[0]) === "string" && typeof(args[1]) === "string") { // Template setter. templateCache[args[0]] = args[1].replace(/^\s+/g, "") .replace(/\s+$/g, "") .replace(/[\n\r]+/g, ""); return templateCache[args[0]]; } else { // Raise an exception because the arguments did not match the // API. throw new TypeError( "jQuery.tempest can't handle the given arguments." ); } } }); // Extend jQuery("selector").tempest using the existing jQuery.tempest API. $.fn.tempest = function() { var args = Array.prototype.slice.call(arguments, 0); if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") { // Inserts the result of rendering the specified template on the // specified data into the set of matched elements. return $(this).html($.tempest(args[0], args[1])); } else if (args.length === 3 && typeof args[0] === "string" && typeof this[args[0]] === "function" && typeof args[1] === "string" && typeof args[2] === "object") { // Calls the appropriate jQuery function, passing it the result of // rendering the given template on the data provided. return $(this)[args[0]]($.tempest(args[1], args[2])); } else { throw new TypeError([ "jQuery(selector).tempest was passed the wrong number or type", "of arguments. Received " + args ].join(" ")); } }; // EXPOSE API TO ALLOW EXTENSION WITH CUSTOM TAGS $.tempest.tags = BLOCK_NODES; $.tempest.getContextValue = getContextValue; $.tempest.TemplateSyntaxError = TemplateSyntaxError; // EXPOSE PRIVATE FUNCTIONS FOR TESTING if (window.testTempestPrivates === true) { $.tempest._test = {}; // Make it easier to attach the private methods methods to the public // object. function a(name, fn) { $.tempest._test[name] = fn; } a("isBlockTag", isBlockTag); a("isEndTag", isEndTag); a("isVarTag", isVarTag); a("cleanVal", cleanVal); a("jQueryToString", jQueryToString); a("makeObj", makeObj); a("storedTemplates", storedTemplates); a("chooseTemplate", chooseTemplate); a("isArray", isArray); a("renderEach", renderEach); a("tokenize", tokenize); a("cdr", cdr); a("append", append); a("makeVarNode", makeVarNode); a("makeTextNode", makeTextNode); a("makeNodes", makeNodes); a("makeBits", makeBits); a("makeBlockNode", makeBlockNode); a("renderToJQ", renderToJQ); a("strip", strip); } // GET ALL TEXTAREA TEMPLATES ON READY $(document).ready(function () { $(".tempest-template").each(function (obj) { templateCache[$(this).attr('title')] = strip(($(this).val() || $(this).html()).replace(/[\n\r]+/g, " ")); $(this).remove(); }); }); }(jQuery)); ================================================ FILE: external/jquery.templates.js ================================================ $.templates = {}; // wycats' templating plugin // (c) Yehuda Katz // You may distribute this code under the same license as jQuery (BSD or GPL) (function ($) { $.compileTemplate = function (template, begin, end) { var rebegin = begin.replace(/([\]{}[\\])/g, '\\$1'); var reend = end.replace(/([\]{}[\\])/g, '\\$1'); var code = "self = self || {}; with ($.templates.helpers) { with (self) {" + "var _result = '';" + template .replace(/[\t\r\n]/g, ' ') .replace(/^(.*)$/, end + '$1' + begin) .replace(new RegExp(reend + "(.*?)" + rebegin, "g"), function (text) { return text .replace(new RegExp("^" + reend + "(.*)" + rebegin + "$"), "$1") .replace(/\\/g, "\\\\") .replace(/'/g, "\\'") .replace(/^(.*)$/, end + "_result += '$1';" + begin); }) .replace(new RegExp(rebegin + "=(.*?)" + reend, "g"), "_result += (function() { if(typeof($1) == 'undefined' || ($1) == null) return ''; else return ($1) })(); ") .replace(new RegExp(rebegin + "(.*?)" + reend, "g"), ' $1 ') .replace(new RegExp("^" + reend + "(.*)" + rebegin + "$"), '$1') + "_result = _result.replace(/^\\s*/, '').replace(/\\s*$/, '');\n" + "if (_rawText) {return _result};\n"+ "var ret = $(_result).data('template_obj', self);\n" + "jQuery(document).trigger('template.created.' + this.templateName, [{ctx: self, el: ret}]);\n" + "return ret;" + "}}"; return new Function("self", "_rawText", code); }; /* Some supplemental useful snippets that help build the widget system */ $(function() { $("script[type=text/x-jquery-template]").each(function() { $.templates[this.title] = $.compileTemplate(this.innerHTML, "<%", "%>"); $.templates[this.title].templateName = this.title; }); }); $.fn.fn = function(name, func) { return this.each(function() { var meths = $(this).data("methods") || $.data(this, "methods", {}); meths[name] = func; }); }; $.fn.invoke = function(name, rest) { meth = $(this).data("methods")[name]; if(!meth) throw new Error("No method by the name of " + name + " exists on this element"); else return meth.apply(this[0], Array.prototype.slice.call(arguments, 1, -1)); }; $.templates = { helpers: { partial: function(name, json) { return $.templates[name](json || {}, true); } } } $.loadTemplates = function() { $.templates = $.templates || {}; $("script[type=text/x-jquery-template]").each(function() { $.templates[this.title] = $.compileTemplate(this.innerHTML, "<%", "%>"); }); } })(jQuery); ================================================ FILE: external/jquery.tmpl.js ================================================ /* * jQuery Templating Plugin * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. */ (function( jQuery, undefined ){ var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /, newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = []; function newTmplItem( options, parentItem, fn, data ) { // Returns a template item data structure for a new rendered instance of a template (a 'template item'). // The content field is a hierarchical array of strings and nested items (to be // removed and replaced by nodes field of dom elements, once inserted in DOM). var newItem = { data: data || (parentItem ? parentItem.data : {}), _wrap: parentItem ? parentItem._wrap : null, tmpl: null, parent: parentItem || null, nodes: [], calls: tiCalls, nest: tiNest, wrap: tiWrap, html: tiHtml, update: tiUpdate }; if ( options ) { jQuery.extend( newItem, options, { nodes: [], parent: parentItem } ); } if ( fn ) { // Build the hierarchical content to be used during insertion into DOM newItem.tmpl = fn; newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem ); newItem.key = ++itemKey; // Keep track of new template item, until it is stored as jQuery Data on DOM element (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem; } return newItem; } // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core). jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems, parent = this.length === 1 && this[0].parentNode; appendToTmplItems = newTmplItems || {}; if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { insert[ original ]( this[0] ); ret = this; } else { for ( i = 0, l = insert.length; i < l; i++ ) { cloneIndex = i; elems = (i > 0 ? this.clone(true) : this).get(); jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); ret = ret.concat( elems ); } cloneIndex = 0; ret = this.pushStack( ret, name, insert.selector ); } tmplItems = appendToTmplItems; appendToTmplItems = null; jQuery.tmpl.complete( tmplItems ); return ret; }; }); jQuery.fn.extend({ // Use first wrapped element as template markup. // Return wrapped set of template items, obtained by rendering template against data. tmpl: function( data, options, parentItem ) { return jQuery.tmpl( this[0], data, options, parentItem ); }, // Find which rendered template item the first wrapped DOM element belongs to tmplItem: function() { return jQuery.tmplItem( this[0] ); }, // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template. template: function( name ) { return jQuery.template( name, this[0] ); }, domManip: function( args, table, callback, options ) { // This appears to be a bug in the appendTo, etc. implementation // it should be doing .call() instead of .apply(). See #6227 if ( args[0] && args[0].nodeType ) { var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem; while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {} if ( argsLength > 1 ) { dmArgs[0] = [jQuery.makeArray( args )]; } if ( tmplItem && cloneIndex ) { dmArgs[2] = function( fragClone ) { // Handler called by oldManip when rendered template has been inserted into DOM. jQuery.tmpl.afterManip( this, fragClone, callback ); }; } oldManip.apply( this, dmArgs ); } else { oldManip.apply( this, arguments ); } cloneIndex = 0; if ( !appendToTmplItems ) { jQuery.tmpl.complete( newTmplItems ); } return this; } }); jQuery.extend({ // Return wrapped set of template items, obtained by rendering template against data. tmpl: function( tmpl, data, options, parentItem ) { var ret, topLevel = !parentItem; if ( topLevel ) { // This is a top-level tmpl call (not from a nested template using {{tmpl}}) parentItem = topTmplItem; tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl ); wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level } else if ( !tmpl ) { // The template item is already associated with DOM - this is a refresh. // Re-evaluate rendered template for the parentItem tmpl = parentItem.tmpl; newTmplItems[parentItem.key] = parentItem; parentItem.nodes = []; if ( parentItem.wrapped ) { updateWrapped( parentItem, parentItem.wrapped ); } // Rebuild, without creating a new template item return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) )); } if ( !tmpl ) { return []; // Could throw... } if ( typeof data === "function" ) { data = data.call( parentItem || {} ); } if ( options && options.wrapped ) { updateWrapped( options, options.wrapped ); } ret = jQuery.isArray( data ) ? jQuery.map( data, function( dataItem ) { return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null; }) : [ newTmplItem( options, parentItem, tmpl, data ) ]; return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret; }, // Return rendered template item for an element. tmplItem: function( elem ) { var tmplItem; if ( elem instanceof jQuery ) { elem = elem[0]; } while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {} return tmplItem || topTmplItem; }, // Set: // Use $.template( name, tmpl ) to cache a named template, // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc. // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration. // Get: // Use $.template( name ) to access a cached template. // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString ) // will return the compiled template, without adding a name reference. // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent // to $.template( null, templateString ) template: function( name, tmpl ) { if (tmpl) { // Compile template and associate with name if ( typeof tmpl === "string" ) { // This is an HTML string being passed directly in. tmpl = buildTmplFn( tmpl ) } else if ( tmpl instanceof jQuery ) { tmpl = tmpl[0] || {}; } if ( tmpl.nodeType ) { // If this is a template block, use cached copy, or generate tmpl function and cache. tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML )); } return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl; } // Return named compiled template return name ? (typeof name !== "string" ? jQuery.template( null, name ): (jQuery.template[name] || // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; }, encode: function( text ) { // Do HTML encoding replacing < > & and ' and " by corresponding entities. return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'"); } }); jQuery.extend( jQuery.tmpl, { tag: { "tmpl": { _default: { $2: "null" }, open: "if($notnull_1){_=_.concat($item.nest($1,$2));}" // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions) // This means that {{tmpl foo}} treats foo as a template (which IS a function). // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}. }, "wrap": { _default: { $2: "null" }, open: "$item.calls(_,$1,$2);_=[];", close: "call=$item.calls();_=call._.concat($item.wrap(call,_));" }, "each": { _default: { $2: "$index, $value" }, open: "if($notnull_1){$.each($1a,function($2){with(this){", close: "}});}" }, "if": { open: "if(($notnull_1) && $1a){", close: "}" }, "else": { _default: { $1: "true" }, open: "}else if(($notnull_1) && $1a){" }, "html": { // Unecoded expression evaluation. open: "if($notnull_1){_.push($1a);}" }, "=": { // Encoded expression evaluation. Abbreviated form is ${}. _default: { $1: "$data" }, open: "if($notnull_1){_.push($.encode($1a));}" }, "!": { // Comment tag. Skipped by parser open: "" } }, // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events complete: function( items ) { newTmplItems = {}; }, // Call this from code which overrides domManip, or equivalent // Manage cloning/storing template items etc. afterManip: function afterManip( elem, fragClone, callback ) { // Provides cloned fragment ready for fixup prior to and after insertion into DOM var content = fragClone.nodeType === 11 ? jQuery.makeArray(fragClone.childNodes) : fragClone.nodeType === 1 ? [fragClone] : []; // Return fragment to original caller (e.g. append) for DOM insertion callback.call( elem, fragClone ); // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data. storeTmplItems( content ); cloneIndex++; } }); //========================== Private helper functions, used by code above ========================== function build( tmplItem, nested, content ) { // Convert hierarchical content into flat string array // and finally return array of fragments ready for DOM insertion var frag, ret = content ? jQuery.map( content, function( item ) { return (typeof item === "string") ? // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM. (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) : // This is a child template item. Build nested template. build( item, tmplItem, item._ctnt ); }) : // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. tmplItem; if ( nested ) { return ret; } // top-level template ret = ret.join(""); // Support templates which have initial or final text nodes, or consist only of text // Also support HTML entities within the HTML markup. ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) { frag = jQuery( middle ).get(); storeTmplItems( frag ); if ( before ) { frag = unencode( before ).concat(frag); } if ( after ) { frag = frag.concat(unencode( after )); } }); return frag ? frag : unencode( ret ); } function unencode( text ) { // Use createElement, since createTextNode will not render HTML entities correctly var el = document.createElement( "div" ); el.innerHTML = text; return jQuery.makeArray(el.childNodes); } // Generate a reusable function that will serve to render a template against data function buildTmplFn( markup ) { return new Function("jQuery","$item", "var $=jQuery,call,_=[],$data=$item.data;" + // Introduce the data as local variables using with(){} "with($data){_.push('" + // Convert the template into pure JavaScript jQuery.trim(markup) .replace( /([\\'])/g, "\\$1" ) .replace( /[\r\t\n]/g, " " ) .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" ) .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g, function( all, slash, type, fnargs, target, parens, args ) { var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect; if ( !tag ) { throw "Template command not found: " + type; } def = tag._default || []; if ( parens && !/\w$/.test(target)) { target += parens; parens = ""; } if ( target ) { target = unescape( target ); args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : ""); // Support for target being things like a.toLowerCase(); // In that case don't call with template item as 'this' pointer. Just evaluate... expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target; exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))"; } else { exprAutoFnDetect = expr = def.$1 || "null"; } fnargs = unescape( fnargs ); return "');" + tag[ slash ? "close" : "open" ] .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" ) .split( "$1a" ).join( exprAutoFnDetect ) .split( "$1" ).join( expr ) .split( "$2" ).join( fnargs ? fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) { params = params ? ("," + params + ")") : (parens ? ")" : ""); return params ? ("(" + name + ").call($item" + params) : all; }) : (def.$2||"") ) + "_.push('"; }) + "');}return _;" ); } function updateWrapped( options, wrapped ) { // Build the wrapped content. options._wrap = build( options, true, // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string. jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()] ).join(""); } function unescape( args ) { return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null; } function outerHtml( elem ) { var div = document.createElement("div"); div.appendChild( elem.cloneNode(true) ); return div.innerHTML; } // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance. function storeTmplItems( content ) { var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m; for ( i = 0, l = content.length; i < l; i++ ) { if ( (elem = content[i]).nodeType !== 1 ) { continue; } elems = elem.getElementsByTagName("*"); for ( m = elems.length - 1; m >= 0; m-- ) { processItemKey( elems[m] ); } processItemKey( elem ); } function processItemKey( el ) { var pntKey, pntNode = el, pntItem, tmplItem, key; // Ensure that each rendered template inserted into the DOM has its own template item, if ( (key = el.getAttribute( tmplItmAtt ))) { while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { } if ( pntKey !== key ) { // The next ancestor with a _tmplitem expando is on a different key than this one. // So this is a top-level element within this template item // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment. pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0; if ( !(tmplItem = newTmplItems[key]) ) { // The item is for wrapped content, and was copied from the temporary parent wrappedItem. tmplItem = wrappedItems[key]; tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true ); tmplItem.key = ++itemKey; newTmplItems[itemKey] = tmplItem; } if ( cloneIndex ) { cloneTmplItem( key ); } } el.removeAttribute( tmplItmAtt ); } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) { // This was a rendered element, cloned during append or appendTo etc. // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem. cloneTmplItem( tmplItem.key ); newTmplItems[tmplItem.key] = tmplItem; pntNode = jQuery.data( el.parentNode, "tmplItem" ); pntNode = pntNode ? pntNode.key : 0; } if ( tmplItem ) { pntItem = tmplItem; // Find the template item of the parent element. // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string) while ( pntItem && pntItem.key != pntNode ) { // Add this element as a top-level node for this rendered template item, as well as for any // ancestor items between this item and the item of its parent element pntItem.nodes.push( el ); pntItem = pntItem.parent; } // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering... delete tmplItem._ctnt; delete tmplItem._wrap; // Store template item as jQuery data on the element jQuery.data( el, "tmplItem", tmplItem ); } function cloneTmplItem( key ) { key = key + keySuffix; tmplItem = newClonedItems[key] = (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true )); } } } //---- Helper functions for template item ---- function tiCalls( content, tmpl, data, options ) { if ( !content ) { return stack.pop(); } stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options }); } function tiNest( tmpl, data, options ) { // nested template, using {{tmpl}} tag return jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); } function tiWrap( call, wrapped ) { // nested template, using {{wrap}} tag var options = call.options || {}; options.wrapped = wrapped; // Apply the template, which may incorporate wrapped content, return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item ); } function tiHtml( filter, textOnly ) { var wrapped = this._wrap; return jQuery.map( jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ), function(e) { return textOnly ? e.innerText || e.textContent : e.outerHTML || outerHtml(e); }); } function tiUpdate() { var coll = this.nodes; jQuery.tmpl( null, null, null, this).insertBefore( coll[0] ); jQuery( coll ).remove(); } })( jQuery ); ================================================ FILE: external/pure.js ================================================ /*! PURE Unobtrusive Rendering Engine for HTML Licensed under the MIT licenses. More information at: http://www.opensource.org Copyright (c) 2010 Michael Cvilic - BeeBole.com Thanks to Rog Peppe for the functional JS jump revision: 2.47 */ var $p, pure = $p = function(){ var sel = arguments[0], ctxt = false; if(typeof sel === 'string'){ ctxt = arguments[1] || false; } return $p.core(sel, ctxt); }; $p.core = function(sel, ctxt, plugins){ //get an instance of the plugins var plugins = getPlugins(), templates = []; //search for the template node(s) switch(typeof sel){ case 'string': templates = plugins.find(ctxt || document, sel); if(templates.length === 0) { error('The template "' + sel + '" was not found'); } break; case 'undefined': error('The template root is undefined, check your selector'); break; default: templates = [sel]; } for(var i = 0, ii = templates.length; i < ii; i++){ plugins[i] = templates[i]; } plugins.length = ii; // set the signature string that will be replaced at render time var Sig = '_s' + Math.floor( Math.random() * 1000000 ) + '_', // another signature to prepend to attributes and avoid checks: style, height, on[events]... attPfx = '_a' + Math.floor( Math.random() * 1000000 ) + '_', // rx to parse selectors, e.g. "+tr.foo[class]" selRx = /^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/, // set automatically attributes for some tags autoAttr = { IMG:'src', INPUT:'value' }, // check if the argument is an array - thanks salty-horse (Ori Avtalion) isArray = Array.isArray ? function(o) { return Array.isArray(o); } : function(o) { return Object.prototype.toString.call(o) === "[object Array]"; }; return plugins; /* * * * * * * * * * * * * * * * * * * * * * * * * * core functions * * * * * * * * * * * * * * * * * * * * * * * * * */ // error utility function error(e){ if(typeof console !== 'undefined'){ console.log(e); debugger; }else{ alert(e); } throw('pure error: ' + e); } //return a new instance of plugins function getPlugins(){ var plugins = $p.plugins, f = function(){}; f.prototype = plugins; // do not overwrite functions if external definition f.prototype.compile = plugins.compile || compile; f.prototype.render = plugins.render || render; f.prototype.autoRender = plugins.autoRender || autoRender; f.prototype.find = plugins.find || find; // give the compiler and the error handling to the plugin context f.prototype._compiler = compiler; f.prototype._error = error; return new f(); } // returns the outer HTML of a node function outerHTML(node){ // if IE take the internal method otherwise build one return node.outerHTML || ( function(n){ var div = document.createElement('div'), h; div.appendChild( n.cloneNode(true) ); h = div.innerHTML; div = null; return h; })(node); } // returns the string generator function function wrapquote(qfn, f){ return function(ctxt){ return qfn('' + f.call(ctxt.context, ctxt)); }; } // default find using querySelector when available on the browser function find(n, sel){ if(typeof n === 'string'){ sel = n; n = false; } if(typeof document.querySelectorAll !== 'undefined'){ return (n||document).querySelectorAll( sel ); }else{ error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine'); } } // create a function that concatenates constant string // sections (given in parts) and the results of called // functions to fill in the gaps between parts (fns). // fns[n] fills in the gap between parts[n-1] and parts[n]; // fns[0] is unused. // this is the inner template evaluation loop. function concatenator(parts, fns){ return function(ctxt){ var strs = [ parts[ 0 ] ], n = parts.length, fnVal, pVal, attLine, pos; for(var i = 1; i < n; i++){ fnVal = fns[i]( ctxt ); pVal = parts[i]; // if the value is empty and attribute, remove it if(fnVal === ''){ attLine = strs[ strs.length - 1 ]; if( ( pos = attLine.search( /[\w]+=\"?$/ ) ) > -1){ strs[ strs.length - 1 ] = attLine.substring( 0, pos ); pVal = pVal.substr( 1 ); } } strs[ strs.length ] = fnVal; strs[ strs.length ] = pVal; } return strs.join(''); }; } // parse and check the loop directive function parseloopspec(p){ var m = p.match( /^(\w+)\s*<-\s*(\S+)?$/ ); if(m === null){ error('bad loop spec: "' + p + '"'); } if(m[1] === 'item'){ error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.'); } if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE) m[2] = function(ctxt){return ctxt.context;}; } return {name: m[1], sel: m[2]}; } // parse a data selector and return a function that // can traverse the data accordingly, given a context. function dataselectfn(sel){ if(typeof(sel) === 'function'){ return sel; } //check for a valid js variable name with hyphen(for properties only), $, _ and : var m = sel.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/); if(m === null){ var found = false, s = sel, parts = [], pfns = [], i = 0, retStr; // check if literal if(/\'|\"/.test( s.charAt(0) )){ if(/\'|\"/.test( s.charAt(s.length-1) )){ retStr = s.substring(1, s.length-1); return function(){ return retStr; }; } }else{ // check if literal + #{var} while((m = s.match(/#\{([^{}]+)\}/)) !== null){ found = true; parts[i++] = s.slice(0, m.index); pfns[i] = dataselectfn(m[1]); s = s.slice(m.index + m[0].length, s.length); } } if(!found){ error('bad data selector syntax: ' + sel); } parts[i] = s; return concatenator(parts, pfns); } m = sel.split('.'); return function(ctxt){ var data = ctxt.context; if(!data){ return ''; } var v = ctxt[m[0]], i = 0; if(v && v.item){ data = v.item; i += 1; } var n = m.length; for(; i < n; i++){ if(!data){break;} data = data[m[i]]; } return (!data && data !== 0) ? '':data; }; } // wrap in an object the target node/attr and their properties function gettarget(dom, sel, isloop){ var osel, prepend, selector, attr, append, target = []; if( typeof sel === 'string' ){ osel = sel; var m = sel.match(selRx); if( !m ){ error( 'bad selector syntax: ' + sel ); } prepend = m[1]; selector = m[2]; attr = m[3]; append = m[4]; if(selector === '.' || ( !selector && attr ) ){ target[0] = dom; }else{ target = plugins.find(dom, selector); } if(!target || target.length === 0){ return error('The node "' + sel + '" was not found in the template'); } }else{ // autoRender node prepend = sel.prepend; attr = sel.attr; append = sel.append; target = [dom]; } if( prepend || append ){ if( prepend && append ){ error('append/prepend cannot take place at the same time'); }else if( isloop ){ error('no append/prepend/replace modifiers allowed for loop target'); }else if( append && isloop ){ error('cannot append with loop (sel: ' + osel + ')'); } } var setstr, getstr, quotefn, isStyle, isClass, attName, setfn; if(attr){ isStyle = (/^style$/i).test(attr); isClass = (/^class$/i).test(attr); attName = isClass ? 'className' : attr; setstr = function(node, s) { node.setAttribute(attPfx + attr, s); if (attName in node && !isStyle) { node[attName] = ''; } if (node.nodeType === 1) { node.removeAttribute(attr); isClass && node.removeAttribute(attName); } }; if (isStyle || isClass) {//IE no quotes special care if(isStyle){ getstr = function(n){ return n.style.cssText; }; }else{ getstr = function(n){ return n.className; }; } quotefn = function(s){ return s.replace(/\"/g, '"'); }; }else { getstr = function(n){ return n.getAttribute(attr); }; quotefn = function(s){ return s.replace(/\"/g, '"').replace(/\s/g, ' '); }; } if(prepend){ setfn = function(node, s){ setstr( node, s + getstr( node )); }; }else if(append){ setfn = function(node, s){ setstr( node, getstr( node ) + s); }; }else{ setfn = function(node, s){ setstr( node, s ); }; } }else{ if (isloop) { setfn = function(node, s) { var pn = node.parentNode; if (pn) { //replace node with s pn.insertBefore(document.createTextNode(s), node.nextSibling); pn.removeChild(node); } }; } else { if (prepend) { setfn = function(node, s) { node.insertBefore(document.createTextNode(s), node.firstChild); }; } else if (append) { setfn = function(node, s) { node.appendChild(document.createTextNode(s));}; } else { setfn = function(node, s) { while (node.firstChild) { node.removeChild(node.firstChild); } node.appendChild(document.createTextNode(s)); }; } } quotefn = function(s) { return s; }; } return { attr: attr, nodes: target, set: setfn, sel: osel, quotefn: quotefn }; } function setsig(target, n){ var sig = Sig + n + ':'; for(var i = 0; i < target.nodes.length; i++){ // could check for overlapping targets here. target.set( target.nodes[i], sig ); } } // read de loop data, and pass it to the inner rendering function function loopfn(name, dselect, inner, sorter, filter){ return function(ctxt){ var a = dselect(ctxt), old = ctxt[name], temp = { items : a }, filtered = 0, length, strs = [], buildArg = function(idx, temp, ftr, len){ ctxt.pos = temp.pos = idx; ctxt.item = temp.item = a[ idx ]; ctxt.items = a; //if array, set a length property - filtered items typeof len !== 'undefined' && (ctxt.length = len); //if filter directive if(typeof ftr === 'function' && ftr(ctxt) === false){ filtered++; return; } strs.push( inner.call(temp, ctxt ) ); }; ctxt[name] = temp; if( isArray(a) ){ length = a.length || 0; // if sort directive if(typeof sorter === 'function'){ a.sort(sorter); } //loop on array for(var i = 0, ii = length; i < ii; i++){ buildArg(i, temp, filter, length - filtered); } }else{ if(a && typeof sorter !== 'undefined'){ error('sort is only available on arrays, not objects'); } //loop on collections for(var prop in a){ a.hasOwnProperty( prop ) && buildArg(prop, temp, filter); } } typeof old !== 'undefined' ? ctxt[name] = old : delete ctxt[name]; return strs.join(''); }; } // generate the template for a loop node function loopgen(dom, sel, loop, fns){ var already = false, ls, sorter, filter, prop; for(prop in loop){ if(loop.hasOwnProperty(prop)){ if(prop === 'sort'){ sorter = loop.sort; continue; }else if(prop === 'filter'){ filter = loop.filter; continue; } if(already){ error('cannot have more than one loop on a target'); } ls = prop; already = true; } } if(!ls){ error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)'); } var dsel = loop[ls]; // if it's a simple data selector then we default to contents, not replacement. if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){ loop = {}; loop[ls] = {root: dsel}; return loopgen(dom, sel, loop, fns); } var spec = parseloopspec(ls), itersel = dataselectfn(spec.sel), target = gettarget(dom, sel, true), nodes = target.nodes; for(i = 0; i < nodes.length; i++){ var node = nodes[i], inner = compiler(node, dsel); fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter)); target.nodes = [node]; // N.B. side effect on target. setsig(target, fns.length - 1); } } function getAutoNodes(n, data){ var ns = n.getElementsByTagName('*'), an = [], openLoops = {a:[],l:{}}, cspec, isNodeValue, i, ii, j, jj, ni, cs, cj; //for each node found in the template for(i = -1, ii = ns.length; i < ii; i++){ ni = i > -1 ?ns[i]:n; if(ni.nodeType === 1 && ni.className !== ''){ //when a className is found cs = ni.className.split(' '); // for each className for(j = 0, jj=cs.length;j -1 || isNodeValue){ ni.className = ni.className.replace('@'+cspec.attr, ''); if(isNodeValue){ cspec.attr = false; } } an.push({n:ni, cspec:cspec}); } } } } return an; function checkClass(c, tagName){ // read the class var ca = c.match(selRx), attr = ca[3] || autoAttr[tagName], cspec = {prepend:!!ca[1], prop:ca[2], attr:attr, append:!!ca[4], sel:c}, i, ii, loopi, loopil, val; // check in existing open loops for(i = openLoops.a.length-1; i >= 0; i--){ loopi = openLoops.a[i]; loopil = loopi.l[0]; val = loopil && loopil[cspec.prop]; if(typeof val !== 'undefined'){ cspec.prop = loopi.p + '.' + cspec.prop; if(openLoops.l[cspec.prop] === true){ val = val[0]; } break; } } // not found check first level of data if(typeof val === 'undefined'){ val = isArray(data) ? data[0][cspec.prop] : data[cspec.prop]; // nothing found return if(typeof val === 'undefined'){ return false; } } // set the spec for autoNode if(isArray(val)){ openLoops.a.push( {l:val, p:cspec.prop} ); openLoops.l[cspec.prop] = true; cspec.t = 'loop'; }else{ cspec.t = 'str'; } return cspec; } } // returns a function that, given a context argument, // will render the template defined by dom and directive. function compiler(dom, directive, data, ans){ var fns = []; // autoRendering nodes parsing -> auto-nodes ans = ans || data && getAutoNodes(dom, data); if(data){ var j, jj, cspec, n, target, nodes, itersel, node, inner; // for each auto-nodes while(ans.length > 0){ cspec = ans[0].cspec; n = ans[0].n; ans.splice(0, 1); if(cspec.t === 'str'){ // if the target is a value target = gettarget(n, cspec, false); setsig(target, fns.length); fns[fns.length] = wrapquote(target.quotefn, dataselectfn(cspec.prop)); }else{ // if the target is a loop itersel = dataselectfn(cspec.sel); target = gettarget(n, cspec, true); nodes = target.nodes; for(j = 0, jj = nodes.length; j < jj; j++){ node = nodes[j]; inner = compiler(node, false, data, ans); fns[fns.length] = wrapquote(target.quotefn, loopfn(cspec.sel, itersel, inner)); target.nodes = [node]; setsig(target, fns.length - 1); } } } } // read directives var target, dsel; for(var sel in directive){ if(directive.hasOwnProperty(sel)){ dsel = directive[sel]; if(typeof(dsel) === 'function' || typeof(dsel) === 'string'){ // set the value for the node/attr target = gettarget(dom, sel, false); setsig(target, fns.length); fns[fns.length] = wrapquote(target.quotefn, dataselectfn(dsel)); }else{ // loop on node loopgen(dom, sel, dsel, fns); } } } // convert node to a string var h = outerHTML(dom), pfns = []; // IE adds an unremovable "selected, value" attribute // hard replace while waiting for a better solution h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>"); // remove attribute prefix h = h.split(attPfx).join(''); // slice the html string at "Sig" var parts = h.split( Sig ), p; // for each slice add the return string of for(var i = 1; i < parts.length; i++){ p = parts[i]; // part is of the form "fn-number:..." as placed there by setsig. pfns[i] = fns[ parseInt(p, 10) ]; parts[i] = p.substring( p.indexOf(':') + 1 ); } return concatenator(parts, pfns); } // compile the template with directive // if a context is passed, the autoRendering is triggered automatically // return a function waiting the data as argument function compile(directive, ctxt, template){ var rfn = compiler( ( template || this[0] ).cloneNode(true), directive, ctxt); return function(context){ console.log(rfn); return rfn({context:context}); }; } //compile with the directive as argument // run the template function on the context argument // return an HTML string // should replace the template and return this function render(ctxt, directive){ var fn = typeof directive === 'function' ? directive : plugins.compile( directive, false, this[0] ); for(var i = 0, ii = this.length; i < ii; i++){ this[i] = replaceWith( this[i], fn( ctxt, false )); } context = null; return this; } // compile the template with autoRender // run the template function on the context argument // return an HTML string function autoRender(ctxt, directive){ var fn = plugins.compile( directive, ctxt, this[0] ); for(var i = 0, ii = this.length; i < ii; i++){ this[i] = replaceWith( this[i], fn( ctxt, false)); } context = null; return this; } function replaceWith(elm, html) { var ne, ep = elm.parentNode, depth = 0; switch (elm.tagName) { case 'TBODY': case 'THEAD': case 'TFOOT': html = '' + html + '
'; depth = 1; break; case 'TR': html = '' + html + '
'; depth = 2; break; case 'TD': case 'TH': html = '' + html + '
'; depth = 3; break; } tmp = document.createElement('SPAN'); tmp.style.display = 'none'; document.body.appendChild(tmp); tmp.innerHTML = html; ne = tmp.firstChild; while (depth--) { ne = ne.firstChild; } ep.insertBefore(ne, elm); ep.removeChild(elm); document.body.removeChild(tmp); elm = ne; ne = ep = null; return elm; } }; $p.plugins = {}; $p.libs = { dojo:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return dojo.query(sel, n); }; } }, domassistant:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return $(n).cssSelect(sel); }; } DOMAssistant.attach({ publicMethods : [ 'compile', 'render', 'autoRender'], compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, render:function(ctxt, directive){ return $( $p(this).render(ctxt, directive) )[0]; }, autoRender:function(ctxt, directive){ return $( $p(this).autoRender(ctxt, directive) )[0]; } }); }, jquery:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return jQuery(n).find(sel); }; } jQuery.fn.extend({ compile:function(directive, ctxt){ return $p(this[0]).compile(directive, ctxt); }, render:function(ctxt, directive){ return jQuery( $p( this[0] ).render( ctxt, directive ) ); }, autoRender:function(ctxt, directive){ return jQuery( $p( this[0] ).autoRender( ctxt, directive ) ); } }); }, mootools:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return $(n).getElements(sel); }; } Element.implement({ compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, render:function(ctxt, directive){ return $p(this).render(ctxt, directive); }, autoRender:function(ctxt, directive){ return $p(this).autoRender(ctxt, directive); } }); }, prototype:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ n = n === document ? n.body : n; return typeof n === 'string' ? $$(n) : $(n).select(sel); }; } Element.addMethods({ compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); }, render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); }, autoRender:function(element, ctxt, directive){ return $p(element).autoRender(ctxt, directive); } }); }, sizzle:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return Sizzle(sel, n); }; } }, sly:function(){ if(typeof document.querySelector === 'undefined'){ $p.plugins.find = function(n, sel){ return Sly(sel, n); }; } } }; // get lib specifics if available (function(){ var libkey = typeof dojo !== 'undefined' && 'dojo' || typeof DOMAssistant !== 'undefined' && 'domassistant' || typeof jQuery !== 'undefined' && 'jquery' || typeof MooTools !== 'undefined' && 'mootools' || typeof Prototype !== 'undefined' && 'prototype' || typeof Sizzle !== 'undefined' && 'sizzle' || typeof Sly !== 'undefined' && 'sly'; libkey && $p.libs[libkey](); })(); ================================================ FILE: external/qunit.css ================================================ ol#qunit-tests { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; margin:0; padding:0; list-style-position:inside; font-size: smaller; } ol#qunit-tests li{ padding:0.4em 0.5em 0.4em 2.5em; border-bottom:1px solid #fff; font-size:small; list-style-position:inside; } ol#qunit-tests li ol{ box-shadow: inset 0px 2px 13px #999; -moz-box-shadow: inset 0px 2px 13px #999; -webkit-box-shadow: inset 0px 2px 13px #999; margin-top:0.5em; margin-left:0; padding:0.5em; background-color:#fff; border-radius:15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; } ol#qunit-tests li li{ border-bottom:none; margin:0.5em; background-color:#fff; list-style-position: inside; padding:0.4em 0.5em 0.4em 0.5em; } ol#qunit-tests li li.pass{ border-left:26px solid #C6E746; background-color:#fff; color:#5E740B; } ol#qunit-tests li li.fail{ border-left:26px solid #EE5757; background-color:#fff; color:#710909; } ol#qunit-tests li.pass{ background-color:#C6E746; color:#000; } ol#qunit-tests li.fail{ background-color:#EE5757; color:#000; } ol#qunit-tests li strong { cursor:pointer; } h1#qunit-header{ background-color:#0d3349; margin:0; padding:0.5em 0 0.5em 1em; color:#fff; font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; border-top-right-radius:15px; border-top-left-radius:15px; -moz-border-radius-topright:15px; -moz-border-radius-topleft:15px; -webkit-border-top-right-radius:15px; -webkit-border-top-left-radius:15px; text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px; } h2#qunit-banner{ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; height:5px; margin:0; padding:0; } h2#qunit-banner.qunit-pass{ background-color:#C6E746; } h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar { background-color:#EE5757; } #qunit-testrunner-toolbar { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; padding:0; /*width:80%;*/ padding:0em 0 0.5em 2em; font-size: small; } h2#qunit-userAgent { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; background-color:#2b81af; margin:0; padding:0; color:#fff; font-size: small; padding:0.5em 0 0.5em 2.5em; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } p#qunit-testresult{ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; margin:0; font-size: small; color:#2b81af; border-bottom-right-radius:15px; border-bottom-left-radius:15px; -moz-border-radius-bottomright:15px; -moz-border-radius-bottomleft:15px; -webkit-border-bottom-right-radius:15px; -webkit-border-bottom-left-radius:15px; background-color:#D2E0E6; padding:0.5em 0.5em 0.5em 2.5em; } strong b.fail{ color:#710909; } strong b.pass{ color:#5E740B; } ================================================ FILE: external/qunit.js ================================================ /* * QUnit - A JavaScript Unit Testing Framework * * http://docs.jquery.com/QUnit * * Copyright (c) 2009 John Resig, Jörn Zaefferer * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. */ (function(window) { var QUnit = { // Initialize the configuration options init: function() { config = { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: +new Date, updateRate: 1000, blocking: false, autorun: false, assertions: [], filters: [], queue: [] }; var tests = id("qunit-tests"), banner = id("qunit-banner"), result = id("qunit-testresult"); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } }, // call on start of module test to prepend name to all tests module: function(name, testEnvironment) { config.currentModule = name; synchronize(function() { if ( config.currentModule ) { QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); } config.currentModule = name; config.moduleTestEnvironment = testEnvironment; config.moduleStats = { all: 0, bad: 0 }; QUnit.moduleStart( name, testEnvironment ); }); }, asyncTest: function(testName, expected, callback) { if ( arguments.length === 2 ) { callback = expected; expected = 0; } QUnit.test(testName, expected, callback, true); }, test: function(testName, expected, callback, async) { var name = testName, testEnvironment, testEnvironmentArg; if ( arguments.length === 2 ) { callback = expected; expected = null; } // is 2nd argument a testEnvironment? if ( expected && typeof expected === 'object') { testEnvironmentArg = expected; expected = null; } if ( config.currentModule ) { name = config.currentModule + " module: " + name; } if ( !validTest(name) ) { return; } synchronize(function() { QUnit.testStart( testName ); testEnvironment = extend({ setup: function() {}, teardown: function() {} }, config.moduleTestEnvironment); if (testEnvironmentArg) { extend(testEnvironment,testEnvironmentArg); } // allow utility functions to access the current test environment QUnit.current_testEnvironment = testEnvironment; config.assertions = []; config.expected = expected; try { if ( !config.pollution ) { saveGlobal(); } testEnvironment.setup.call(testEnvironment); } catch(e) { QUnit.ok( false, "Setup failed on " + name + ": " + e.message ); } if ( async ) { QUnit.stop(); } try { callback.call(testEnvironment); } catch(e) { fail("Test " + name + " died, exception and test follows", e, callback); QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { start(); } } }); synchronize(function() { try { checkPollution(); testEnvironment.teardown.call(testEnvironment); } catch(e) { QUnit.ok( false, "Teardown failed on " + name + ": " + e.message ); } try { QUnit.reset(); } catch(e) { fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset); } if ( config.expected && config.expected != config.assertions.length ) { QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" ); } var good = 0, bad = 0, tests = id("qunit-tests"); config.stats.all += config.assertions.length; config.moduleStats.all += config.assertions.length; if ( tests ) { var ol = document.createElement("ol"); ol.style.display = "none"; for ( var i = 0; i < config.assertions.length; i++ ) { var assertion = config.assertions[i]; var li = document.createElement("li"); li.className = assertion.result ? "pass" : "fail"; li.appendChild(document.createTextNode(assertion.message || "(no message)")); ol.appendChild( li ); if ( assertion.result ) { good++; } else { bad++; config.stats.bad++; config.moduleStats.bad++; } } var b = document.createElement("strong"); b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")"; addEvent(b, "click", function() { var next = b.nextSibling, display = next.style.display; next.style.display = display === "none" ? "block" : "none"; }); addEvent(b, "dblclick", function(e) { var target = e && e.target ? e.target : window.event.srcElement; if ( target.nodeName.toLowerCase() === "strong" ) { var text = "", node = target.firstChild; while ( node.nodeType === 3 ) { text += node.nodeValue; node = node.nextSibling; } text = text.replace(/(^\s*|\s*$)/g, ""); if ( window.location ) { window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text); } } }); var li = document.createElement("li"); li.className = bad ? "fail" : "pass"; li.appendChild( b ); li.appendChild( ol ); tests.appendChild( li ); if ( bad ) { var toolbar = id("qunit-testrunner-toolbar"); if ( toolbar ) { toolbar.style.display = "block"; id("qunit-filter-pass").disabled = null; id("qunit-filter-missing").disabled = null; } } } else { for ( var i = 0; i < config.assertions.length; i++ ) { if ( !config.assertions[i].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } } QUnit.testDone( testName, bad, config.assertions.length ); if ( !window.setTimeout && !config.queue.length ) { done(); } }); if ( window.setTimeout && !config.doneTimer ) { config.doneTimer = window.setTimeout(function(){ if ( !config.queue.length ) { done(); } else { synchronize( done ); } }, 13); } }, /** * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. */ expect: function(asserts) { config.expected = asserts; }, /** * Asserts true. * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); */ ok: function(a, msg) { QUnit.log(a, msg); config.assertions.push({ result: !!a, message: msg }); }, /** * Checks that the first two arguments are equal, with an optional message. * Prints out both actual and expected values. * * Prefered to ok( actual == expected, message ) * * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); * * @param Object actual * @param Object expected * @param String message (optional) */ equal: function(actual, expected, message) { push(expected == actual, actual, expected, message); }, notEqual: function(actual, expected, message) { push(expected != actual, actual, expected, message); }, deepEqual: function(a, b, message) { push(QUnit.equiv(a, b), a, b, message); }, notDeepEqual: function(a, b, message) { push(!QUnit.equiv(a, b), a, b, message); }, strictEqual: function(actual, expected, message) { push(expected === actual, actual, expected, message); }, notStrictEqual: function(actual, expected, message) { push(expected !== actual, actual, expected, message); }, start: function() { // A slight delay, to avoid any current callbacks if ( window.setTimeout ) { window.setTimeout(function() { if ( config.timeout ) { clearTimeout(config.timeout); } config.blocking = false; process(); }, 13); } else { config.blocking = false; process(); } }, stop: function(timeout) { config.blocking = true; if ( timeout && window.setTimeout ) { config.timeout = window.setTimeout(function() { QUnit.ok( false, "Test timed out" ); QUnit.start(); }, timeout); } }, /** * Resets the test setup. Useful for tests that modify the DOM. */ reset: function() { if ( window.jQuery ) { jQuery("#main").html( config.fixture ); jQuery.event.global = {}; jQuery.ajaxSettings = extend({}, config.ajaxSettings); } }, /** * Trigger an event on an element. * * @example triggerEvent( document.body, "click" ); * * @param DOMElement elem * @param String type */ triggerEvent: function( elem, type, event ) { if ( document.createEvent ) { event = document.createEvent("MouseEvents"); event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); elem.dispatchEvent( event ); } else if ( elem.fireEvent ) { elem.fireEvent("on"+type); } }, // Safe object type checking is: function( type, obj ) { return Object.prototype.toString.call( obj ) === "[object "+ type +"]"; }, // Logging callbacks done: function(failures, total) {}, log: function(result, message) {}, testStart: function(name) {}, testDone: function(name, failures, total) {}, moduleStart: function(name, testEnvironment) {}, moduleDone: function(name, failures, total) {} }; // Backwards compatibility, deprecated QUnit.equals = QUnit.equal; QUnit.same = QUnit.deepEqual; // Maintain internal state var config = { // The queue of tests to run queue: [], // block until document ready blocking: true }; // Load paramaters (function() { var location = window.location || { search: "", protocol: "file:" }, GETParams = location.search.slice(1).split('&'); for ( var i = 0; i < GETParams.length; i++ ) { GETParams[i] = decodeURIComponent( GETParams[i] ); if ( GETParams[i] === "noglobals" ) { GETParams.splice( i, 1 ); i--; config.noglobals = true; } else if ( GETParams[i].search('=') > -1 ) { GETParams.splice( i, 1 ); i--; } } // restrict modules/tests by get parameters config.filters = GETParams; // Figure out if we're running the tests from a server or not QUnit.isLocal = !!(location.protocol === 'file:'); })(); // Expose the API as global variables, unless an 'exports' // object exists, in that case we assume we're in CommonJS if ( typeof exports === "undefined" || typeof require === "undefined" ) { extend(window, QUnit); window.QUnit = QUnit; } else { extend(exports, QUnit); exports.QUnit = QUnit; } if ( typeof document === "undefined" || document.readyState === "complete" ) { config.autorun = true; } addEvent(window, "load", function() { // Initialize the config, saving the execution queue var oldconfig = extend({}, config); QUnit.init(); extend(config, oldconfig); config.blocking = false; var userAgent = id("qunit-userAgent"); if ( userAgent ) { userAgent.innerHTML = navigator.userAgent; } var toolbar = id("qunit-testrunner-toolbar"); if ( toolbar ) { toolbar.style.display = "none"; var filter = document.createElement("input"); filter.type = "checkbox"; filter.id = "qunit-filter-pass"; filter.disabled = true; addEvent( filter, "click", function() { var li = document.getElementsByTagName("li"); for ( var i = 0; i < li.length; i++ ) { if ( li[i].className.indexOf("pass") > -1 ) { li[i].style.display = filter.checked ? "none" : ""; } } }); toolbar.appendChild( filter ); var label = document.createElement("label"); label.setAttribute("for", "qunit-filter-pass"); label.innerHTML = "Hide passed tests"; toolbar.appendChild( label ); var missing = document.createElement("input"); missing.type = "checkbox"; missing.id = "qunit-filter-missing"; missing.disabled = true; addEvent( missing, "click", function() { var li = document.getElementsByTagName("li"); for ( var i = 0; i < li.length; i++ ) { if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) { li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block"; } } }); toolbar.appendChild( missing ); label = document.createElement("label"); label.setAttribute("for", "qunit-filter-missing"); label.innerHTML = "Hide missing tests (untested code is broken code)"; toolbar.appendChild( label ); } var main = id('main'); if ( main ) { config.fixture = main.innerHTML; } if ( window.jQuery ) { config.ajaxSettings = window.jQuery.ajaxSettings; } QUnit.start(); }); function done() { if ( config.doneTimer && window.clearTimeout ) { window.clearTimeout( config.doneTimer ); config.doneTimer = null; } if ( config.queue.length ) { config.doneTimer = window.setTimeout(function(){ if ( !config.queue.length ) { done(); } else { synchronize( done ); } }, 13); return; } config.autorun = true; // Log the last module results if ( config.currentModule ) { QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); } var banner = id("qunit-banner"), tests = id("qunit-tests"), html = ['Tests completed in ', +new Date - config.started, ' milliseconds.
', '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); if ( banner ) { banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); } if ( tests ) { var result = id("qunit-testresult"); if ( !result ) { result = document.createElement("p"); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests.nextSibling ); } result.innerHTML = html; } QUnit.done( config.stats.bad, config.stats.all ); } function validTest( name ) { var i = config.filters.length, run = false; if ( !i ) { return true; } while ( i-- ) { var filter = config.filters[i], not = filter.charAt(0) == '!'; if ( not ) { filter = filter.slice(1); } if ( name.indexOf(filter) !== -1 ) { return !not; } if ( not ) { run = true; } } return run; } function push(result, actual, expected, message) { message = message || (result ? "okay" : "failed"); QUnit.ok( result, result ? message + ": " + QUnit.jsDump.parse(expected) : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) ); } function synchronize( callback ) { config.queue.push( callback ); if ( config.autorun && !config.blocking ) { process(); } } function process() { var start = (new Date()).getTime(); while ( config.queue.length && !config.blocking ) { if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { config.queue.shift()(); } else { setTimeout( process, 13 ); break; } } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in window ) { config.pollution.push( key ); } } } function checkPollution( name ) { var old = config.pollution; saveGlobal(); var newGlobals = diff( old, config.pollution ); if ( newGlobals.length > 0 ) { ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); config.expected++; } var deletedGlobals = diff( config.pollution, old ); if ( deletedGlobals.length > 0 ) { ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); config.expected++; } } // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var result = a.slice(); for ( var i = 0; i < result.length; i++ ) { for ( var j = 0; j < b.length; j++ ) { if ( result[i] === b[j] ) { result.splice(i, 1); i--; break; } } } return result; } function fail(message, exception, callback) { if ( typeof console !== "undefined" && console.error && console.warn ) { console.error(message); console.error(exception); console.warn(callback.toString()); } else if ( window.opera && opera.postError ) { opera.postError(message, exception, callback.toString); } } function extend(a, b) { for ( var prop in b ) { a[prop] = b[prop]; } return a; } function addEvent(elem, type, fn) { if ( elem.addEventListener ) { elem.addEventListener( type, fn, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, fn ); } else { fn(); } } function id(name) { return !!(typeof document !== "undefined" && document && document.getElementById) && document.getElementById( name ); } // Test for equality any JavaScript type. // Discussions and reference: http://philrathe.com/articles/equiv // Test suites: http://philrathe.com/tests/equiv // Author: Philippe Rathé QUnit.equiv = function () { var innerEquiv; // the real equiv function var callers = []; // stack to decide between skip/abort functions var parents = []; // stack to avoiding loops from circular referencing // Determine what is o. function hoozit(o) { if (QUnit.is("String", o)) { return "string"; } else if (QUnit.is("Boolean", o)) { return "boolean"; } else if (QUnit.is("Number", o)) { if (isNaN(o)) { return "nan"; } else { return "number"; } } else if (typeof o === "undefined") { return "undefined"; // consider: typeof null === object } else if (o === null) { return "null"; // consider: typeof [] === object } else if (QUnit.is( "Array", o)) { return "array"; // consider: typeof new Date() === object } else if (QUnit.is( "Date", o)) { return "date"; // consider: /./ instanceof Object; // /./ instanceof RegExp; // typeof /./ === "function"; // => false in IE and Opera, // true in FF and Safari } else if (QUnit.is( "RegExp", o)) { return "regexp"; } else if (typeof o === "object") { return "object"; } else if (QUnit.is( "Function", o)) { return "function"; } else { return undefined; } } // Call the o related callback with the given arguments. function bindCallbacks(o, callbacks, args) { var prop = hoozit(o); if (prop) { if (hoozit(callbacks[prop]) === "function") { return callbacks[prop].apply(callbacks, args); } else { return callbacks[prop]; // or undefined } } } var callbacks = function () { // for string, boolean, number and null function useStrictEquality(b, a) { if (b instanceof a.constructor || a instanceof b.constructor) { // to catch short annotaion VS 'new' annotation of a declaration // e.g. var i = 1; // var j = new Number(1); return a == b; } else { return a === b; } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function (b) { return isNaN(b); }, "date": function (b, a) { return hoozit(b) === "date" && a.valueOf() === b.valueOf(); }, "regexp": function (b, a) { return hoozit(b) === "regexp" && a.source === b.source && // the regex itself a.global === b.global && // and its modifers (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline; }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function () { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined"; }, "array": function (b, a) { var i, j, loop; var len; // b could be an object literal here if ( ! (hoozit(b) === "array")) { return false; } len = a.length; if (len !== b.length) { // safe and faster return false; } //track reference to avoid circular references parents.push(a); for (i = 0; i < len; i++) { loop = false; for(j=0;j= 0) { type = "array"; } else { type = typeof obj; } return type; }, separator:function() { return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; }, indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing if ( !this.multiline ) return ''; var chr = this.indentChar; if ( this.HTML ) chr = chr.replace(/\t/g,' ').replace(/ /g,' '); return Array( this._depth_ + (extra||0) ).join(chr); }, up:function( a ) { this._depth_ += a || 1; }, down:function( a ) { this._depth_ -= a || 1; }, setParser:function( name, parser ) { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote:quote, literal:literal, join:join, // _depth_: 1, // This is the list of parsers, to modify them, use jsDump.setParser parsers:{ window: '[Window]', document: '[Document]', error:'[ERROR]', //when no parser is found, shouldn't happen unknown: '[Unknown]', 'null':'null', undefined:'undefined', 'function':function( fn ) { var ret = 'function', name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE if ( name ) ret += ' ' + name; ret += '('; ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); return join( ret, this.parse(fn,'functionCode'), '}' ); }, array: array, nodelist: array, arguments: array, object:function( map ) { var ret = [ ]; this.up(); for ( var key in map ) ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); this.down(); return join( '{', ret, '}' ); }, node:function( node ) { var open = this.HTML ? '<' : '<', close = this.HTML ? '>' : '>'; var tag = node.nodeName.toLowerCase(), ret = open + tag; for ( var a in this.DOMAttrs ) { var val = node[this.DOMAttrs[a]]; if ( val ) ret += ' ' + a + '=' + this.parse( val, 'attribute' ); } return ret + close + open + '/' + tag + close; }, functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function var l = fn.length; if ( !l ) return ''; var args = Array(l); while ( l-- ) args[l] = String.fromCharCode(97+l);//97 is 'a' return ' ' + args.join(', ') + ' '; }, key:quote, //object calls it internally, the key part of an item in a map functionCode:'[code]', //function calls it internally, it's the content of the function attribute:quote, //node calls it internally, it's an html attribute value string:quote, date:quote, regexp:literal, //regex number:literal, 'boolean':literal }, DOMAttrs:{//attributes to dump from nodes, name=>realName id:'id', name:'name', 'class':'className' }, HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) indentChar:' ',//indentation unit multiline:false //if true, items in a collection, are separated by a \n, else just a space. }; return jsDump; })(); })(this); ================================================ FILE: external/styles.css ================================================ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; text-decoration: none; vertical-align: baseline; background: transparent; font-size: 100%; } acronym { border-bottom: 1px dashed #ccc; cursor: help; } ol, ul { list-style: none; } table { border-collapse: collapse; border-spacing: 0; } html { width: 100%; height: 100%; } body { margin: 1em; color: #0d3349; font: normal .75em/1.5em "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; } h1 { margin: 0; padding: .5em 1em; color: #fff; font-size: 2em; background-color: #0d3349; border-top-right-radius: 1em; border-top-left-radius: 1em; -moz-border-radius-topright: 1em; -moz-border-radius-topleft: 1em; -webkit-border-top-right-radius: 1em; -webkit-border-top-left-radius: 1em; text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px; } h2 { padding: .5em 2em; color: #fff; background-color: #2b81af; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } hr { height: .5em; margin: 0; padding: 0; border: 0 none; background-color: #c6e746; } ul#contestants { margin: 0; padding: 0; font: normal 1em/1.5em monospace, ffmonobug; } ul#contestants > li{ padding: .5em 2em; #font-weight: bold; border-bottom: 1px solid #fff; background-color: #c6e746; } ul.progress { margin: 0; padding: 0; vertical-align: middle; display: inline; } ul.progress li { width: 1.167em; height: 1.167em; margin: 0 1px 0 0; padding: 0; display: inline-block; background-color: #0d3349; } p.number { margin: 0 0 0 .5em; display: inline; } ul.srender li { background-color: #5b4cd8; } ul.mustache_js li { background-color: #8f04a8; } ul.underscore li { background-color: #cd0074; } ul.jqote2 li { background-color: #70e500; } ul.tempest li { background-color: #0d3349; } ul.nano li { background-color: #fff800; } ul.tmpl li { background-color: #00f8ff; } ul.ejs li { background-color: #f800ff; } ul.pure li { background-color: #ff9900; } ul input { margin: 0 .5em 0 0; vertical-align: middle; } #placeholder { width: 800px; height: 350px; margin: 0 auto; } #pure { display: none; } ================================================ FILE: external/underscore.js ================================================ // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: // http://documentcloud.github.com/underscore (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `global` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Establish the object that gets thrown to break out of a loop iteration. var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__'; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype; // Create quick reference variables for speed access to core prototypes. var slice = ArrayProto.slice, unshift = ArrayProto.unshift, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty, propertyIsEnumerable = ObjProto.propertyIsEnumerable; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeForEach = ArrayProto.forEach, nativeMap = ArrayProto.map, nativeReduce = ArrayProto.reduce, nativeReduceRight = ArrayProto.reduceRight, nativeFilter = ArrayProto.filter, nativeEvery = ArrayProto.every, nativeSome = ArrayProto.some, nativeIndexOf = ArrayProto.indexOf, nativeLastIndexOf = ArrayProto.lastIndexOf, nativeIsArray = Array.isArray, nativeKeys = Object.keys; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { return new wrapper(obj); }; // Export the Underscore object for **CommonJS**. if (typeof exports !== 'undefined') exports._ = _; // Export Underscore to the global scope. root._ = _; // Current version. _.VERSION = '1.1.1'; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles objects implementing `forEach`, arrays, and raw objects. // Delegates to **ECMAScript 5**'s native `forEach` if available. var each = _.each = _.forEach = function(obj, iterator, context) { try { if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (_.isNumber(obj.length)) { for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj); } else { for (var key in obj) { if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj); } } } catch(e) { if (e != breaker) throw e; } return obj; }; // Return the results of applying the iterator to each element. // Delegates to **ECMAScript 5**'s native `map` if available. _.map = function(obj, iterator, context) { if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); var results = []; each(obj, function(value, index, list) { results[results.length] = iterator.call(context, value, index, list); }); return results; }; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { if (nativeReduce && obj.reduce === nativeReduce) { if (context) iterator = _.bind(iterator, context); return obj.reduce(iterator, memo); } each(obj, function(value, index, list) { memo = iterator.call(context, memo, value, index, list); }); return memo; }; // The right-associative version of reduce, also known as `foldr`. // Delegates to **ECMAScript 5**'s native `reduceRight` if available. _.reduceRight = _.foldr = function(obj, iterator, memo, context) { if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { if (context) iterator = _.bind(iterator, context); return obj.reduceRight(iterator, memo); } var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse(); return _.reduce(reversed, iterator, memo, context); }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, iterator, context) { var result; each(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) { result = value; _.breakLoop(); } }); return result; }; // Return all the elements that pass a truth test. // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. _.filter = _.select = function(obj, iterator, context) { if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); var results = []; each(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) results[results.length] = value; }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, iterator, context) { var results = []; each(obj, function(value, index, list) { if (!iterator.call(context, value, index, list)) results[results.length] = value; }); return results; }; // Determine whether all of the elements match a truth test. // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. _.every = _.all = function(obj, iterator, context) { iterator = iterator || _.identity; if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); var result = true; each(obj, function(value, index, list) { if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop(); }); return result; }; // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. _.some = _.any = function(obj, iterator, context) { iterator = iterator || _.identity; if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); var result = false; each(obj, function(value, index, list) { if (result = iterator.call(context, value, index, list)) _.breakLoop(); }); return result; }; // Determine if a given value is included in the array or object using `===`. _.include = function(obj, target) { if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; var found = false; each(obj, function(value) { if (found = value === target) _.breakLoop(); }); return found; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); return _.map(obj, function(value) { return (method ? value[method] : value).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, function(value){ return value[key]; }); }; // Return the maximum element or (element-based computation). _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); var result = {computed : -Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed >= result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Return the minimum element (or element-based computation). _.min = function(obj, iterator, context) { if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); var result = {computed : Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Sort the object's values by a criterion produced by an iterator. _.sortBy = function(obj, iterator, context) { return _.pluck(_.map(obj, function(value, index, list) { return { value : value, criteria : iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; // Use a comparator function to figure out at what index an object should // be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator) { iterator = iterator || _.identity; var low = 0, high = array.length; while (low < high) { var mid = (low + high) >> 1; iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; } return low; }; // Safely convert anything iterable into a real, live array. _.toArray = function(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); if (_.isArray(iterable)) return iterable; if (_.isArguments(iterable)) return slice.call(iterable); return _.values(iterable); }; // Return the number of elements in an object. _.size = function(obj) { return _.toArray(obj).length; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head`. The **guard** check allows it to work // with `_.map`. _.first = _.head = function(array, n, guard) { return n && !guard ? slice.call(array, 0, n) : array[0]; }; // Returns everything but the first entry of the array. Aliased as `tail`. // Especially useful on the arguments object. Passing an **index** will return // the rest of the values in the array from that index onward. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = function(array, index, guard) { return slice.call(array, _.isUndefined(index) || guard ? 1 : index); }; // Get the last element of an array. _.last = function(array) { return array[array.length - 1]; }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, function(value){ return !!value; }); }; // Return a completely flattened version of an array. _.flatten = function(array) { return _.reduce(array, function(memo, value) { if (_.isArray(value)) return memo.concat(_.flatten(value)); memo[memo.length] = value; return memo; }, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { var values = slice.call(arguments, 1); return _.filter(array, function(value){ return !_.include(values, value); }); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. _.uniq = function(array, isSorted) { return _.reduce(array, function(memo, el, i) { if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; return memo; }, []); }; // Produce an array that contains every item shared between all the // passed-in arrays. Aliased as `contains`. _.intersect = _.contains = function(array) { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { return _.indexOf(other, item) >= 0; }); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { var args = slice.call(arguments); var length = _.max(_.pluck(args, 'length')); var results = new Array(length); for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); return results; }; // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // we need this function. Return the position of the first occurence of an // item in an array, or -1 if the item is not included in the array. // Delegates to **ECMAScript 5**'s native `indexOf` if available. _.indexOf = function(array, item) { if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; return -1; }; // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. _.lastIndexOf = function(array, item) { if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); var i = array.length; while (i--) if (array[i] === item) return i; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { var args = slice.call(arguments), solo = args.length <= 1, start = solo ? 0 : args[0], stop = solo ? args[0] : args[1], step = args[2] || 1, len = Math.max(Math.ceil((stop - start) / step), 0), idx = 0, range = new Array(len); while (idx < len) { range[idx++] = start; start += step; } return range; }; // Function (ahem) Functions // ------------------ // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Binding with arguments is also known as `curry`. _.bind = function(func, obj) { var args = slice.call(arguments, 2); return function() { return func.apply(obj || {}, args.concat(slice.call(arguments))); }; }; // Bind all of an object's methods to that object. Useful for ensuring that // all callbacks defined on an object belong to it. _.bindAll = function(obj) { var funcs = slice.call(arguments, 1); if (funcs.length == 0) funcs = _.functions(obj); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memo = {}; hasher = hasher || _.identity; return function() { var key = hasher.apply(this, arguments); return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments)); }; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(func, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return function() { var args = [func].concat(slice.call(arguments)); return wrapper.apply(wrapper, args); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var funcs = slice.call(arguments); return function() { var args = slice.call(arguments); for (var i=funcs.length-1; i >= 0; i--) { args = [funcs[i].apply(this, args)]; } return args[0]; }; }; // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = nativeKeys || function(obj) { if (_.isArray(obj)) return _.range(0, obj.length); var keys = []; for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key; return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { return _.map(obj, _.identity); }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { each(slice.call(arguments, 1), function(source) { for (var prop in source) obj[prop] = source[prop]; }); return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { // Check object identity. if (a === b) return true; // Different types? var atype = typeof(a), btype = typeof(b); if (atype != btype) return false; // Basic equality test (watch out for coercions). if (a == b) return true; // One is falsy and the other truthy. if ((!a && b) || (a && !b)) return false; // One of them implements an isEqual()? if (a.isEqual) return a.isEqual(b); // Check dates' integer values. if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); // Both are NaN? if (_.isNaN(a) && _.isNaN(b)) return false; // Compare regular expressions. if (_.isRegExp(a) && _.isRegExp(b)) return a.source === b.source && a.global === b.global && a.ignoreCase === b.ignoreCase && a.multiline === b.multiline; // If a is not an object by this point, we can't handle it. if (atype !== 'object') return false; // Check for different array lengths before comparing contents. if (a.length && (a.length !== b.length)) return false; // Nothing else worked, deep compare the contents. var aKeys = _.keys(a), bKeys = _.keys(b); // Different object sizes? if (aKeys.length != bKeys.length) return false; // Recursive comparison of contents. for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false; return true; }; // Is a given array or object empty? _.isEmpty = function(obj) { if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType == 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return !!(obj && obj.concat && obj.unshift && !obj.callee); }; // Is a given variable an arguments object? _.isArguments = function(obj) { return !!(obj && obj.callee); }; // Is a given value a function? _.isFunction = function(obj) { return !!(obj && obj.constructor && obj.call && obj.apply); }; // Is a given value a string? _.isString = function(obj) { return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); }; // Is a given value a number? _.isNumber = function(obj) { return (obj === +obj) || (toString.call(obj) === '[object Number]'); }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false; }; // Is a given value a date? _.isDate = function(obj) { return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear); }; // Is the given value a regular expression? _.isRegExp = function(obj) { return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); }; // Is the given value NaN -- this one is interesting. NaN != NaN, and // isNaN(undefined) == true, so we make sure it's a number first. _.isNaN = function(obj) { return _.isNumber(obj) && isNaN(obj); }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return typeof obj == 'undefined'; }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iterators. _.identity = function(value) { return value; }; // Run a function **n** times. _.times = function (n, iterator, context) { for (var i = 0; i < n; i++) iterator.call(context, i); }; // Break out of the middle of an iteration. _.breakLoop = function() { throw breaker; }; // Add your own custom functions to the Underscore object, ensuring that // they're correctly added to the OOP wrapper as well. _.mixin = function(obj) { each(_.functions(obj), function(name){ addToWrapper(name, _[name] = obj[name]); }); }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = idCounter++; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. _.template = function(str, data) { var c = _.templateSettings; var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/'/g, "\\'") .replace(c.interpolate, function(match, code) { return "'," + code.replace(/\\'/g, "'") + ",'"; }) .replace(c.evaluate || null, function(match, code) { return "');" + code.replace(/\\'/g, "'") .replace(/[\r\n\t]/g, ' ') + "__p.push('"; }) .replace(/\r/g, '\\r') .replace(/\n/g, '\\n') .replace(/\t/g, '\\t') + "');}return __p.join('');"; var func = new Function('obj', tmpl); return data ? func(data) : func; }; // The OOP Wrapper // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. var wrapper = function(obj) { this._wrapped = obj; }; // Expose `wrapper.prototype` as `_.prototype` _.prototype = wrapper.prototype; // Helper function to continue chaining intermediate results. var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }; // A method to easily add functions to the OOP wrapper. var addToWrapper = function(name, func) { wrapper.prototype[name] = function() { var args = slice.call(arguments); unshift.call(args, this._wrapped); return result(func.apply(_, args), this._chain); }; }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { method.apply(this._wrapped, arguments); return result(this._wrapped, this._chain); }; }); // Add all accessor Array functions to the wrapper. each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { return result(method.apply(this._wrapped, arguments), this._chain); }; }); // Start chaining a wrapped Underscore object. wrapper.prototype.chain = function() { this._chain = true; return this; }; // Extracts the result from a wrapped and chained object. wrapper.prototype.value = function() { return this._wrapped; }; })(); ================================================ FILE: jqote.benchmark.htm ================================================  ECMAScript Templating Benchmarks // aefxx.com

ECMAScript Templating Benchmarks


©2010 aefxx // powered by jQuery // idea taken from Brian Landau

  • srender

    • mustache.js

      • Underscore

        • jQote2

          • Tempest

            • nano

              • jQuery Templating Plugin

                • embeddedjavascript

                  • pure

                    • Check all
                    •   Cycles:  5x 10x 25x 50x   Types:  Simple Loop

                    █ Single Passed Run    Median in ms [Arithm. AVG in ms]

                    The homepage is .

                    • <%= this.comments[n].name %>

                      <%= this.comments[n].body %>

                    ================================================ FILE: jqote.qunit.htm ================================================ jQote2 QUnit Simple Test Suite // aefxx.com

                    jQote2 QUnit Simple Test Suite

                      ================================================ FILE: jquery.jqote2.js ================================================ /* * jQote2 - client-side Javascript templating engine * Copyright (C) 2010, aefxx * http://aefxx.com/ * * Dual licensed under the WTFPL v2 or MIT (X11) licenses * WTFPL v2 Copyright (C) 2004, Sam Hocevar * * Date: Fri, May 4th, 2012 * Version: 0.9.8 */ (function($) { var JQOTE2_TMPL_UNDEF_ERROR = 'UndefinedTemplateError', JQOTE2_TMPL_COMP_ERROR = 'TemplateCompilationError', JQOTE2_TMPL_EXEC_ERROR = 'TemplateExecutionError'; var ARR = '[object Array]', STR = '[object String]', FUNC = '[object Function]'; var n = 1, tag = '%', qreg = /^[^<]*(<[\w\W]+>)[^>]*$/, type_of = Object.prototype.toString; function raise(error, ext) { throw ($.extend(error, ext), error); } function dotted_ns(fn) { var ns = []; if ( type_of.call(fn) !== ARR ) return false; for ( var i=0,l=fn.length; i < l; i++ ) ns[i] = fn[i].jqote_id; return ns.length ? ns.sort().join('.').replace(/(\b\d+\b)\.(?:\1(\.|$))+/g, '$1$2') : false; } function lambda(tmpl, t) { var f, fn = [], t = t || tag, type = type_of.call(tmpl); if ( type === FUNC ) return tmpl.jqote_id ? [tmpl] : false; if ( type !== ARR ) return [$.jqotec(tmpl, t)]; if ( type === ARR ) for ( var i=0,l=tmpl.length; i < l; i++ ) if ( f = lambda(tmpl[i], t) ) fn.push(f[0]); return fn.length ? fn : false; } $.fn.extend({ jqote: function(data, t) { var data = type_of.call(data) === ARR ? data : [data], dom = ''; this.each(function(i) { var fn = $.jqotec(this, t); for ( var j=0; j < data.length; j++ ) dom += fn.call(data[j], i, j, data, fn); }); return dom; } }); $.each({app: 'append', pre: 'prepend', sub: 'html'}, function(name, method) { $.fn['jqote'+name] = function(elem, data, t) { var ns, regexp, str = $.jqote(elem, data, t), $$ = !qreg.test(str) ? function(str) {return $(document.createTextNode(str));} : $; if ( !!(ns = dotted_ns(lambda(elem))) ) regexp = new RegExp('(^|\\.)'+ns.split('.').join('\\.(.*)?')+'(\\.|$)'); return this.each(function() { var dom = $$(str); $(this)[method](dom); ( dom[0].nodeType === 3 ? $(this) : dom ).trigger('jqote.'+name, [dom, regexp]); }); }; }); $.extend({ jqote: function(elem, data, t) { var str = '', t = t || tag, fn = lambda(elem, t); if ( fn === false ) raise(new Error('Empty or undefined template passed to $.jqote'), {type: JQOTE2_TMPL_UNDEF_ERROR}); data = type_of.call(data) !== ARR ? [data] : data; for ( var i=0,l=fn.length; i < l; i++ ) for ( var j=0; j < data.length; j++ ) str += fn[i].call(data[j], i, j, data, fn[i]); return str; }, jqotec: function(template, t) { var cache, elem, tmpl, t = t || tag, type = type_of.call(template); if ( type === STR && qreg.test(template) ) { elem = tmpl = template; if ( cache = $.jqotecache[template] ) return cache; } else { elem = type === STR || template.nodeType ? $(template) : template instanceof jQuery ? template : null; if ( !elem[0] || !(tmpl = elem[0].innerHTML) && !(tmpl = elem.text()) ) raise(new Error('Empty or undefined template passed to $.jqotec'), {type: JQOTE2_TMPL_UNDEF_ERROR}); if ( cache = $.jqotecache[$.data(elem[0], 'jqote_id')] ) return cache; } var str = '', index, arr = tmpl.replace(/\s*\s*|[\r\n\t]/g, '') .split('<'+t).join(t+'>\x1b') .split(t+'>'); for ( var m=0,l=arr.length; m < l; m++ ) str += arr[m].charAt(0) !== '\x1b' ? "out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ? ';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ? ';out+=$.jqotenc((' + arr[m].substr(2) + '));' : ';' + arr[m].substr(1))); str = 'try{' + ('var out="";'+str+';return out;') .split("out+='';").join('') .split('var out="";out+=').join('var out=') + '}catch(e){e.type="'+JQOTE2_TMPL_EXEC_ERROR+'";e.args=arguments;e.template=arguments.callee.toString();throw e;}'; try { var fn = new Function('i, j, data, fn', str); } catch ( e ) { raise(e, {type: JQOTE2_TMPL_COMP_ERROR}); } index = elem instanceof jQuery ? $.data(elem[0], 'jqote_id', n) : elem; return $.jqotecache[index] = (fn.jqote_id = n++, fn); }, jqotefn: function(elem) { var type = type_of.call(elem), index = type === STR && qreg.test(elem) ? elem : $.data($(elem)[0], 'jqote_id'); return $.jqotecache[index] || false; }, jqotetag: function(str) { if ( type_of.call(str) === STR ) tag = str; }, jqotenc: function(str) { return str.toString() .replace(/&(?!\w+;)/g, '&') .split('<').join('<').split('>').join('>') .split('"').join('"').split("'").join('''); }, jqotecache: {} }); $.event.special.jqote = { add: function(obj) { var ns, handler = obj.handler, data = !obj.data ? [] : type_of.call(obj.data) !== ARR ? [obj.data] : obj.data; if ( !obj.namespace ) obj.namespace = 'app.pre.sub'; if ( !data.length || !(ns = dotted_ns(lambda(data))) ) return; obj.handler = function(event, dom, regexp) { return !regexp || regexp.test(ns) ? handler.apply(this, [event, dom]) : null; }; } }; })(jQuery); ================================================ FILE: version.txt ================================================ 0.9.8