Full Code of aefxx/jQote2 for AI

master 474253a3f737 cached
20 files
175.1 KB
47.1k tokens
77 symbols
1 requests
Download .txt
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;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof source=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=options.cache!=null?options.cache:EJS.cache;EJS.type=options.type!=null?options.type:EJS.type;EJS.ext=options.ext!=null?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return }templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)}})();EJS.Helpers.prototype.date_tag=function(C,O,A){if(!(O instanceof Date)){O=new Date()}var B=["January","February","March","April","May","June","July","August","September","October","November","December"];var G=[],D=[],P=[];var J=O.getFullYear();var H=O.getMonth();var N=O.getDate();for(var M=J-15;M<J+15;M++){G.push({value:M,text:M})}for(var E=0;E<12;E++){D.push({value:(E),text:B[E]})}for(var I=0;I<31;I++){P.push({value:(I+1),text:(I+1)})}var L=this.select_tag(C+"[year]",J,G,{id:C+"[year]"});var F=this.select_tag(C+"[month]",H,D,{id:C+"[month]"});var K=this.select_tag(C+"[day]",N,P,{id:C+"[day]"});return L+F+K};EJS.Helpers.prototype.form_tag=function(B,A){A=A||{};A.action=B;if(A.multipart==true){A.method="post";A.enctype="multipart/form-data"}return this.start_tag_for("form",A)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(A,C,B){return this.input_field_tag(A,C,"hidden",B)};EJS.Helpers.prototype.input_field_tag=function(A,D,C,B){B=B||{};B.id=B.id||A;B.value=D||"";B.type=C||"text";B.name=A;return this.single_tag_for("input",B)};EJS.Helpers.prototype.is_current_page=function(A){return(window.location.href==A||window.location.pathname==A?true:false)};EJS.Helpers.prototype.link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.href=A;return this.start_tag_for("a",C)+B+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}C.onclick=C.onclick||"";if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.value=B;C.type="submit";C.onclick=C.onclick+(A?this.url_for(A):"")+"return false;";return this.start_tag_for("input",C)};EJS.Helpers.prototype.link_to_if=function(F,B,A,D,C,E){return this.link_to_unless((F==false),B,A,D,C,E)};EJS.Helpers.prototype.link_to_unless=function(E,B,A,C,D){C=C||{};if(E){if(D&&typeof D=="function"){return D(B,A,C,D)}else{return B}}else{return this.link_to(B,A,C)}};EJS.Helpers.prototype.link_to_unless_current=function(B,A,C,D){C=C||{};return this.link_to_unless(this.is_current_page(A),B,A,C,D)};EJS.Helpers.prototype.password_field_tag=function(A,C,B){return this.input_field_tag(A,C,"password",B)};EJS.Helpers.prototype.select_tag=function(D,G,H,F){F=F||{};F.id=F.id||D;F.value=G;F.name=D;var B="";B+=this.start_tag_for("select",F);for(var E=0;E<H.length;E++){var C=H[E];var A={value:C.value};if(C.value==G){A.selected="selected"}B+=this.start_tag_for("option",A)+C.text+this.tag_end("option")}B+=this.tag_end("select");return B};EJS.Helpers.prototype.single_tag_for=function(A,B){return this.tag(A,B,"/>")};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"</"+A+">"};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 "&amp;";
        case "\\": return "\\\\";
        case '"': return '\"';
        case "<": return "&lt;";
        case ">": return "&gt;";
        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 <item> in <array> %}"
                        );
                    }
                }
            },
            "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("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
		}
	});

	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, '&quot;'); };
			}else {
				getstr = function(n){ return n.getAttribute(attr); };
				quotefn = function(s){ return s.replace(/\"/g, '&quot;').replace(/\s/g, '&nbsp;'); };
			}
			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<jj;j++){
					cj = cs[j];
					// check if it is related to a context property
					cspec = checkClass(cj, ni.tagName);
					// if so, store the node, plus the type of data
					if(cspec !== false){
						isNodeValue = (/nodevalue/i).test(cspec.attr);
						if(cspec.sel.indexOf('@') > -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 = '<TABLE>' + html + '</TABLE>';
				depth = 1;
			break;
			case 'TR':
				html = '<TABLE><TBODY>' + html + '</TBODY></TABLE>';
				depth = 2;
			break;
			case 'TD': case 'TH':
				html = '<TABLE><TBODY><TR>' + html + '</TR></TBODY></TABLE>';
				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 + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
				
				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.<br/>',
		'<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> 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é <prathe@gmail.com>
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<parents.length;j++){
                        if(parents[j] === a[i]){
                            loop = true;//dont rewalk array
                        }
                    }
                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        parents.pop();
                        return false;
                    }
                }
                parents.pop();
                return true;
            },

            "object": function (b, a) {
                var i, j, loop;
                var eq = true; // unless we can proove it
                var aProperties = [], bProperties = []; // collection of strings

                // comparing constructors is more strict than using instanceof
                if ( a.constructor !== b.constructor) {
                    return false;
                }

                // stack constructor before traversing properties
                callers.push(a.constructor);
                //track reference to avoid circular references
                parents.push(a);
                
                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i])
                            loop = true; //don't go down the same path twice
                    }
                    aProperties.push(i); // collect a's properties

                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        eq = false;
                        break;
                    }
                }

                callers.pop(); // unstack, we are done
                parents.pop();

                for (i in b) {
                    bProperties.push(i); // collect b's properties
                }

                // Ensures identical properties name
                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
            }
        };
    }();

    innerEquiv = function () { // can take multiple arguments
        var args = Array.prototype.slice.apply(arguments);
        if (args.length < 2) {
            return true; // end transition
        }

        return (function (a, b) {
            if (a === b) {
                return true; // catch the most you can
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
                return false; // don't lose time with error prone cases
            } else {
                return bindCallbacks(a, callbacks, [b, a]);
            }

        // apply transition with (1..n) arguments
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
    };

    return innerEquiv;

}();

/**
 * jsDump
 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
 * Date: 5/15/2008
 * @projectDescription Advanced and extensible data dumping for Javascript.
 * @version 1.0.0
 * @author Ariel Flesler
 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
 */
QUnit.jsDump = (function() {
	function quote( str ) {
		return '"' + str.toString().replace(/"/g, '\\"') + '"';
	};
	function literal( o ) {
		return o + '';	
	};
	function join( pre, arr, post ) {
		var s = jsDump.separator(),
			base = jsDump.indent(),
			inner = jsDump.indent(1);
		if ( arr.join )
			arr = arr.join( ',' + s + inner );
		if ( !arr )
			return pre + post;
		return [ pre, inner + arr, base + post ].join(s);
	};
	function array( arr ) {
		var i = arr.length,	ret = Array(i);					
		this.up();
		while ( i-- )
			ret[i] = this.parse( arr[i] );				
		this.down();
		return join( '[', ret, ']' );
	};
	
	var reName = /^function (\w+)/;
	
	var jsDump = {
		parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
			var	parser = this.parsers[ type || this.typeOf(obj) ];
			type = typeof parser;			
			
			return type == 'function' ? parser.call( this, obj ) :
				   type == 'string' ? parser :
				   this.parsers.error;
		},
		typeOf:function( obj ) {
			var type;
			if ( obj === null ) {
				type = "null";
			} else if (typeof obj === "undefined") {
				type = "undefined";
			} else if (QUnit.is("RegExp", obj)) {
				type = "regexp";
			} else if (QUnit.is("Date", obj)) {
				type = "date";
			} else if (QUnit.is("Function", obj)) {
				type = "function";
			} else if (obj.setInterval && obj.document && !obj.nodeType) {
				type = "window";
			} else if (obj.nodeType === 9) {
				type = "document";
			} else if (obj.nodeType) {
				type = "node";
			} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
				type = "array";
			} else {
				type = typeof obj;
			}
			return type;
		},
		separator:function() {
			return this.multiline ?	this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
		},
		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,'&nbsp;');
			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 ? '&lt;' : '<',
					close = this.HTML ? '&gt;' : '>';
					
				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
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
        <title>ECMAScript Templating Benchmarks // aefxx.com</title>
        <link type="image/x-icon" href="favicon.ico" rel="Shortcut Icon">
        <link rel="stylesheet" href="external/styles.css" type="text/css"/>
        <script src="external/jquery-1.4.2.min.js" type="text/javascript"></script>
        <script src="external/jquery.benchmark.js" type="text/javascript"></script>
        <script src="external/jquery.flot.min.js" type="text/javascript"></script>
        <script src="external/jquery.mustache.js" type="text/javascript"></script>
        <script src="external/jquery.tempest.js" type="text/javascript"></script>
        <script src="external/jquery.tmpl.js" type="text/javascript"></script>
        <script src="external/underscore.js" type="text/javascript"></script>
        <script src="external/jquery.srender.js" type="text/javascript"></script>
        <script src="external/jquery.nano.js" type="text/javascript"></script>
        <script src="external/ejs_production.js" type="text/javascript"></script>
        <script src="external/pure.js" type="text/javascript"></script>
        <script src="jquery.jqote2.js" type="text/javascript"></script>
    </head>
    <body>
        <h1>ECMAScript Templating Benchmarks</h1>
        <hr/>
        <h2>&copy;2010 aefxx // powered by jQuery // idea taken from Brian Landau</h2>
        <ul id="contestants">
            <li>
                <p><input type="checkbox" name="contestant" value="srender"/> srender</p>
                <ul class="progress srender"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="mustache_js"/> mustache.js</p>
                <ul class="progress mustache_js"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="underscore"/> Underscore</p>
                <ul class="progress underscore"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="jqote2"/> jQote2</p>
                <ul class="progress jqote2"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="tempest"/> Tempest</p>
                <ul class="progress tempest"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="nano"/> nano</p>
                <ul class="progress nano"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="tmpl"/> jQuery Templating Plugin</p>
                <ul class="progress tmpl"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="ejs"/> embeddedjavascript</p>
                <ul class="progress ejs"></ul>
                <p class="number"></p>
            </li>
            <li>
                <p><input type="checkbox" name="contestant" value="pure"/> pure</p>
                <ul class="progress pure"></ul>
                <p class="number"></p>
            </li>
            <li>
                <input type="checkbox" name="check" value="0"/> Check all
            </li>
            <li>
                <button id="run">RUN</button>
                &#xa0;&#xa0;Cycles:&#xa0;
                5x <input type="radio" name="cycles" value="5" checked="checked"/>
                10x <input type="radio" name="cycles" value="10"/>
                25x <input type="radio" name="cycles" value="25"/>
                50x <input type="radio" name="cycles" value="50"/>
                &#xa0;&#xa0;Types:&#xa0;
                Simple <input type="checkbox" name="simple_test" value="1" checked="checked"/>
                Loop <input type="checkbox" name="loop_test" value="2" checked="checked"/>
            </li>
        </ul>
        <h2>█ Single Passed Run &#xa0;&#xa0; Median in ms [Arithm. AVG in ms]</h2>
        <div id="placeholder"></div>

        <div id="pure">
            <div class="test">
                <h2></h2>
                <p>The homepage is <a></a>.</p>
                <p></p>
            </div>
            <div class="comments">
                <h3></h3>
                <ul>
                    <li class="comment">
                        <h5><%= this.comments[n].name %></h5>
                        <p><%= this.comments[n].body %></p>
                    </li>
                </ul>
            </div>
        </div>

        <script type="text/x-jqote-template" id="jqote2_simple">
            <![CDATA[<div class="test"><h2>This is a test of <%= this.name %></h2><p>The homepage is <a href="<%= this.url %>"><%= this.url %></a>.</p><p>The sources is: <%= this.source %></p></div>]]>
        </script>

        <script type="text/x-jqote-template" id="jqote2_loop">
            <![CDATA[<div class="comments"><h3><%= this.header %></h3><ul><% for ( var n=0,m=this.comments.length;n<m;n++ ) { %><li class="comment"><h5><%= this.comments[n].name %></h5><p><%= this.comments[n].body %></p></li><% } %></ul></div>]]>
        </script>

        <script type="text/x-ejs" id="ejs_simple">
            <div class="test"><h2>This is a test of [%= name %]</h2><p>The homepage is <a href="[%= url %]">[%= url %]</a>.</p><p>The sources is: [%= source %]</p></div>
        </script>

        <script type="text/x-ejs" id="ejs_loop">
            <div class="comments"><h3>[%= header %]</h3><ul>[% $.each(comments, function(i, comment){ %]<li class="comment"><h5>[%= comment.name %]</h5><p>[%= comment.body %]</p></li>[% }); %]</ul></div>
        </script>

        <script type="text/x-jquery-tmpl" id="tmpl_simple">
            <![CDATA[<div class="test"><h2>This is a test of ${name}</h2><p>The homepage is <a href="${url}">${this.url}</a>.</p><p>The sources is: ${source}</p></div>]]>
        </script>

        <script type="text/x-jquery-tmpl" id="tmpl_loop">
            <![CDATA[<div class="comments"><h3>${header}</h3><ul>{{each(i, comment) comments}}<li class="comment"><h5>${comment.name}</h5><p>${comment.body}</p></li>{{/each}}</ul></div>]]>
        </script>

        <script type="text/javascript">
            var CYCLES = $('input:radio:checked').val(),
                CONVERSIONS = 1000,
                RUN_LEAP = Math.round(CONVERSIONS * 0.5),
                contestants = {};

            function shuffle(v) {
                for ( var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x );
                return v;
            };

            function mean(array) {
                if ( !array.length ) return 0;

                var sum = 0;
                for ( var i=0; i < array.length; i++ )
                    sum += parseFloat(array[i], 4);
                return (1/array.length) * sum;
            }

            function median(array) {
                if ( !array.length ) return 0;

                var s = array.sort(function(a, b) {return a - b;}).length;
                return s % 2 ?
                    array[(s-1)/2] : (array[(s/2)-1] + array[s/2]) / 2;
            }

            function plot(cons) {
                var data = [], i = 1;

                for ( key in cons ) {
                    var result = {
                        median: median(cons[key].results).toPrecision(2)*1000,
                        mean: mean(cons[key].results).toPrecision(2)*1000
                    };

                    data.push({
                        label: cons[key].name,
                        data: [[i++, result.median], [i++,null]],
                        color: cons[key].color,
                        bars: {
                            show: true,
                            barWidth: 1,
                            lineWidth: 1,
                            fill: 1,
                            colors: cons[key].color
                        }
                    });

                    if ( cons[key].results.length )
                        cons[key].number.text(result.median+' ms ['+result.mean+' ms]');
                }

                $.plot($('#placeholder'), data, {
                    xaxis: { ticks: [[1.5, 'Srender'], [3.5, 'mustache.js'], [5.5, 'Underscore'], [7.5, 'jQote2'], [9.5, 'Tempest'], [11.5, 'nano'], [13.5, 'jQuery Templating Plugin'], [15.5, 'embeddedjavascript'], [17.5, 'pure']], autoscaleMargin: .02 },
                    yaxis: { min: 10, max: 150 },
                    legend: { position: 'ne' },
                    grid: {  backgroundColor: '#ffffff' }
                });
            }

            $(function() {
                var benchmarks = {
                    srender: {
                        simple: function() {$.srender(this.simple, payload.simple);},
                        loop: function() {$.srender(this.loop, payload.loop);}
                    },
                    mustache_js: {
                        simple: function() {$.mustache(this.simple, payload.simple);},
                        loop: function() {$.mustache(this.loop, payload.loop);}
                    },
                    underscore: {
                        simple: function() {this.simple(payload.simple);},
                        loop: function() {this.loop(payload.loop);}
                    },
                    jqote2: {
                        simple: function() {$.jqote(this.simple, payload.simple);},
                        loop: function() {$.jqote(this.loop, payload.loop);}
                    },
                    tempest: {
                        simple: function() {$.tempest(this.simple, payload.simple);},
                        loop: function() {$.tempest(this.loop, payload.loop);}
                    },
                    nano: {
                        simple: function() {$.nano(this.simple, payload.simple);},
                        loop: function() {
                            var nano = {comments: '', header: payload.loop.header};
                            for ( var i=0; i < payload.loop.comments.length; i++ )
                                nano.comments += $.nano(this.loop.comment, payload.loop.comments[i]);

                            $.nano(this.loop.container, nano);
                        }
                    },
                    tmpl: {
                        simple: function() {this.simple($, {data: payload.simple}).join("");},
                        loop: function() {this.loop($, {data: payload.loop}).join("");}
                    },
                    ejs: {
                        simple: function() {this.simple.render(payload.simple);},
                        loop: function() {this.loop.render(payload.loop);}
                    },
                    pure: {
                        simple: function() {this.simple(payload.simple);},
                        loop: function() {this.loop(payload.loop);}
                    }
                };

                var templates = {
                    mustache_js: {
                        simple: '<div class="test"><h2>This is a test of {{name}}</h2><p>The homepage is <a href="{{url}}">{{url}}</a>.</p><p>The sources is: {{source}}</p></div>',
                        loop: '<div class="comments"><h3>{{header}}</h3><ul>{{#comments}}<li class="comment"><h5>{{name}}</h5><p>{{body}}</p></li>{{/comments}}</ul></div>'
                    },
                    underscore: {
                        simple: _.template('<div class="test"><h2>This is a test of <%= name %></h2><p>The homepage is <a href="<%= url %>"><%= url %></a>.</p><p>The sources is: <%= source %></p></div>'),
                        loop: _.template('<div class="comments"><h3><%= header %></h3><ul><% _.each(comments, function(comment){ %><li class="comment"><h5><%= comment.name %></h5><p><%= comment.body %></p></li><% }); %></ul></div>')
                    },
                    srender: {
                        simple: '<div class="test"><h2>This is a test of <%= name %></h2><p>The homepage is <a href="<%= url %>"><%= url %></a>.</p><p>The sources is: <%= source %></p></div>',
                        loop: '<div class="comments"><h3><%= header %></h3><ul><% $.each(comments, function(i, comment){ %><li class="comment"><h5><%= comment.name %></h5><p><%= comment.body %></p></li><% }); %></ul></div>'
                    },
                    jqote2: {
                        simple: $.jqotec('#jqote2_simple'),
                        loop: $.jqotec('#jqote2_loop')
                    },
                    tempest: {
                        simple: $.tempest('simple', '<div class="test"><h2>This is a test of {{name}}</h2><p>The homepage is <a href="{{url}}">{{url}}</a>.</p><p>The sources is: {{source}}</p></div>') && 'simple',
                        loop: $.tempest('loop', '<div class="comments"><h3>{{header}}</h3><ul>{% for comment in comments %}<li class="comment"<h5>{{comment.name}}</h5><p>{{comment.body}}</p></li>{% endfor %}</ul></div>') && 'loop'
                    },
                    nano: {
                        simple: '<div class="test"><h2>This is a test of {name}</h2><p>The homepage is <a href="{url}">{url}</a>.</p><p>The sources is: {source}</p></div>',
                        loop: {
                            comment: '<li class="comment"><h5>{name}</h5><p>{body}</p></li>',
                            container: '<div class="comments"><h3>{header}</h3><ul>{comments}</ul></div>'
                        }
                    },
                    tmpl: {
                        simple: $('#tmpl_simple').template(),
                        loop: $('#tmpl_loop').template()
                    },
                    ejs: {
                        simple: new EJS({element: $('#ejs_simple')[0]}),
                        loop: new EJS({element: $('#ejs_loop')[0]})
                    },
                    pure: {
                        simple: $('#pure div.test').compile({
                            'h2': 'This is a test of #{name}',
                            'p a': 'url',
                            'p a@href': 'url',
                            'p:last-child': 'The sources is: #{source}'
                        }),
                        loop: $('#pure div.comments').compile({
                            'h3': 'header',
                            'li': {
                                'comment<-comments': {
                                    'h5': 'comment.name',
                                    'p': 'comment.body'
                                }
                            }
                        })
                    }
                };

                var payload = {
                    simple: {
                        name: 'foo',
                        url: 'http://foo.bar/foo',
                        source: 'http://foo.bar/jquery.foo.js'
                    },
                    loop: {
                        header: "My Post Comments",
                        comments: [
                            {name: "Joe", body: "Thanks for this post!"},
                            {name: "Sam", body: "Thanks for this post!"},
                            {name: "Heather", body: "Thanks for this post!"},
                            {name: "Kathy", body: "Thanks for this post!"},
                            {name: "George", body: "Thanks for this post!"}
                        ]
                    }
                };

                var color = {
                    mustache_js: '#8f04a8',
                    underscore: '#cd0074',
                    srender: '#5b4cd8',
                    jqote2: '#70e500',
                    tempest: '#0d3349',
                    nano: '#fff800',
                    tmpl: '#00f8ff',
                    ejs: '#f800ff',
                    pure: '#ff9900'
                };

                $('input[name=check]').click(function() {
                    var checked = this.checked;

                    $('input[name=contestant]').each(function() {
                        this.checked = checked;
                    });
                });

                $('input[name=contestant]').each(function(i) {
                    var key = this.value;

                    contestants[key] = {
                        name: key,
                        results: [],
                        input: $(this),
                        color: color[key],
                        number: $('p.number', $(this).parents('li')[0]),
                        progress: $('ul.progress', $(this).parents('li')[0]),
                        templates: templates[key],
                        benchmarks: benchmarks[key]
                    };
                }).click(function() {
                    $('input[name=check]')[0].checked = false;
                });

                $('#run').click(function() {
                    CYCLES = $('input:radio:checked').val();
                    $('ul.progress, p.number').empty();
                    $(this).trigger('benchmark');

                }).bind('benchmark', function() {
                    var cons = shuffle($('input[name=contestant]:checked').toArray()),
                        runs = cons.length;

                    if ( !runs ) return;

                    var test_run = setInterval(function() {
                        var contestant = null;

                        if ( !(contestant = cons.shift()) ) return;

                        if ( $('input[name=simple_test]:checked').length )
                            $.benchmark(CONVERSIONS, contestants[contestant.value], 'simple');
                        if ( $('input[name=loop_test]:checked').length )
                            $.benchmark(CONVERSIONS, contestants[contestant.value], 'loop');

                        contestants[contestant.value].progress.append('<li/>');
                    }, RUN_LEAP);

                    setTimeout(function() {
                        clearInterval(test_run);
                        ( --CYCLES ) ? $('#run').trigger('benchmark') : plot(contestants);
                    }, RUN_LEAP * runs + 1500);
                });
            });
        </script>
    </body>
</html>


================================================
FILE: jqote.qunit.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>jQote2 QUnit Simple Test Suite // aefxx.com</title>
        <link type="image/x-icon" href="favicon.ico" rel="Shortcut Icon">
        <link rel="stylesheet" href="external/qunit.css" type="text/css"/>
        <script src="external/jquery-1.4.2.min.js" type="text/javascript"></script>
        <script src="external/qunit.js" type="text/javascript"></script>
        <script src="jquery.jqote2.js" type="text/javascript"></script>
    </head>
    <body>
        <h1 id="qunit-header">jQote2 QUnit Simple Test Suite</h1>
        <h2 id="qunit-banner"></h2>
        <h2 id="qunit-userAgent"></h2>
        <ol id="qunit-tests"></ol>
        <div id="dummy"></div>
        <script type="text/javascript">
            $(function() {
                module('Core');

                test('basic requirements', function() {
                    expect(6);
                    ok( $.fn.jqote, 'jQote2' );
                    ok( $.jqote, 'jQote2 static' );
                    ok( $.fn.jqoteapp, 'jQote2 append' );
                    ok( $.fn.jqotepre, 'jQote2 prepend' );
                    ok( $.fn.jqotesub, 'jQote2 substitute' );
                    ok( $.jqotec, 'jQote2 compile' );
                });

                module('Shorthand tag');

                test('basic tests', function() {
                    expect(3);
                    equal($('#sb1').jqote({str: 'Hello World'}), 'Hello World');
                    equal($('#sb2').jqote({a: 25, b: 2}), '25 % 2 = 1');
                    equal($('#sb3').jqote({
                        a: function(b) {return 'Hello ' + b();},
                        b: function() {return 'Mr. O\'Brian';}
                    }), '"Hello Mr. O\'Brian"');
                });

                test('advanced tests', function() {
                    expect(2);
                    equal($('#sa1').jqote([{x: 0}]), '0123456789');
                    equal($('#sa2').jqote([{n: 7}]), '13');
                });

                module('Standard tag');

                test('basic tests', function() {
                    expect(4);
                    equal($('#stdb1').jqote([{name: 'aefxx'}]), 'Hi, my name is aefxx.');
                    equal($('#stdb2').jqote([{a: 25, b: 2}]), '25 % 2 = 1');
                    equal($('#stdb3').jqote([{x: 3}]), 'IEEE 754');
                    equal($('#stdb4').jqote([{x: 3}]), 'GNU is Not Unix');
                });
            });
        </script>
        <script id="sb1" type="text/x-jqote-template">
            <![CDATA[
                <%= this.str %>
            ]]>
        </script>
        <script id="sb2" type="text/x-jqote-template">
            <![CDATA[
                <%= this.a %> % <%= this.b %> = <%= this.a % this.b %>
            ]]>
        </script>
        <script id="sb3" type="text/x-jqote-template">
            <![CDATA[
                "<%= this.a(this.b) %>"
            ]]>
        </script>
        <script id="sa1" type="text/x-jqote-template">
            <![CDATA[
                <%= this.x + ( j < 8 ? data[j+1] = {x: j+1, y: ''} : {y: '9'}).y %>
            ]]>
        </script>
        <script id="sa2" type="text/x-jqote-template">
            <![CDATA[
                <%= ( ( this.n === 0 ) ?
                        0 : ( this.n === 1 || this.n === 2 ) ?
                            1 : parseInt($.jqote(fn, {n: this.n-1}), 10) + parseInt($.jqote(fn, {n: this.n-2}), 10) )
                %>
            ]]>
        </script>
        <script id="stdb1" type="text/x-jqote-template">
            <![CDATA[
                Hi, my name is <% out += this.name; %>.
            ]]>
        </script>
        <script id="stdb2" type="text/x-jqote-template">
            <![CDATA[
                <% out += this.a; %> % <% out += this.b; %> = <% if ( this.a % this.b ) %>1<% else %>0
            ]]>
        </script>
        <script id="stdb3" type="text/x-jqote-template">
            <![CDATA[
                I<% while ( this.x-- ) %>E<% ; %> 754
            ]]>
        </script>
        <script id="stdb4" type="text/x-jqote-template">
            <![CDATA[
                <% while ( this.x )
                       switch ( this.x-- ) {
                           case 3:
                               %>GNU is <%
                               break;
                           case 2:
                               %>Not <%
                               break;
                           default:
                               %>Unix<%
                       } %>
            ]]>
        </script>
    </body>
</html>


================================================
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*<!\[CDATA\[\s*|\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, '&#38;')
                        .split('<').join('&#60;').split('>').join('&#62;')
                            .split('"').join('&#34;').split("'").join('&#39;');
        },

        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
Download .txt
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
Download .txt
SYMBOL INDEX (77 symbols across 6 files)

FILE: external/jquery.mustache.js
  function is_kinda_truthy (line 221) | function is_kinda_truthy(bool) {

FILE: external/jquery.tempest.js
  function TemplateSyntaxError (line 144) | function TemplateSyntaxError(message) {
  function isBlockTag (line 202) | function isBlockTag(token) {
  function isEndTag (line 205) | function isEndTag(token) {
  function isVarTag (line 208) | function isVarTag(token) {
  function strip (line 212) | function strip(str) {
  function cleanVal (line 217) | function cleanVal(val) {
  function getContextValue (line 233) | function getContextValue(str, context) {
  function jQueryToString (line 252) | function jQueryToString(jq) {
  function makeObj (line 257) | function makeObj(obj) {
  function storedTemplates (line 267) | function storedTemplates() {
  function chooseTemplate (line 277) | function chooseTemplate(str) {
  function isArray (line 284) | function isArray(objToTest) {
  function renderEach (line 292) | function renderEach(data, f) {
  function tokenize (line 300) | function tokenize(templ) {
  function cdr (line 317) | function cdr(arr) {
  function append (line 324) | function append(item, list) {
  function makeVarNode (line 329) | function makeVarNode(token) {
  function makeTextNode (line 337) | function makeTextNode(token) {
  function makeNodes (line 345) | function makeNodes(tokens) {
  function makeBits (line 365) | function makeBits(blockToken) {
  function makeBlockNode (line 381) | function makeBlockNode(nodes, tokens, f) {
  function renderToJQ (line 429) | function renderToJQ(str, objects) {
  function a (line 544) | function a(name, fn) {

FILE: external/jquery.tmpl.js
  function newTmplItem (line 10) | function newTmplItem( options, parentItem, fn, data ) {
  function build (line 270) | function build( tmplItem, nested, content ) {
  function unencode (line 305) | function unencode( text ) {
  function buildTmplFn (line 313) | function buildTmplFn( markup ) {
  function updateWrapped (line 364) | function updateWrapped( options, wrapped ) {
  function unescape (line 372) | function unescape( args ) {
  function outerHtml (line 375) | function outerHtml( elem ) {
  function storeTmplItems (line 382) | function storeTmplItems( content ) {
  function tiCalls (line 450) | function tiCalls( content, tmpl, data, options ) {
  function tiNest (line 457) | function tiNest( tmpl, data, options ) {
  function tiWrap (line 462) | function tiWrap( call, wrapped ) {
  function tiHtml (line 470) | function tiHtml( filter, textOnly ) {
  function tiUpdate (line 481) | function tiUpdate() {

FILE: external/pure.js
  function error (line 77) | function error(e){
  function getPlugins (line 86) | function getPlugins(){
  function outerHTML (line 105) | function outerHTML(node){
  function wrapquote (line 118) | function wrapquote(qfn, f){
  function find (line 125) | function find(n, sel){
  function concatenator (line 143) | function concatenator(parts, fns){
  function parseloopspec (line 170) | function parseloopspec(p){
  function dataselectfn (line 186) | function dataselectfn(sel){
  function gettarget (line 237) | function gettarget(dom, sel, isloop){
  function setsig (line 336) | function setsig(target, n){
  function loopfn (line 345) | function loopfn(name, dselect, inner, sorter, filter){
  function loopgen (line 392) | function loopgen(dom, sel, loop, fns){
  function getAutoNodes (line 434) | function getAutoNodes(n, data){
  function compiler (line 509) | function compiler(dom, directive, data, ans){
  function compile (line 579) | function compile(directive, ctxt, template){
  function render (line 590) | function render(ctxt, directive){
  function autoRender (line 602) | function autoRender(ctxt, directive){
  function replaceWith (line 611) | function replaceWith(elm, html) {

FILE: external/qunit.js
  function done (line 501) | function done() {
  function validTest (line 552) | function validTest( name ) {
  function push (line 580) | function push(result, actual, expected, message) {
  function synchronize (line 585) | function synchronize( callback ) {
  function process (line 593) | function process() {
  function saveGlobal (line 607) | function saveGlobal() {
  function checkPollution (line 617) | function checkPollution( name ) {
  function diff (line 635) | function diff( a, b ) {
  function fail (line 649) | function fail(message, exception, callback) {
  function extend (line 660) | function extend(a, b) {
  function addEvent (line 668) | function addEvent(elem, type, fn) {
  function id (line 678) | function id(name) {
  function hoozit (line 695) | function hoozit(o) {
  function bindCallbacks (line 743) | function bindCallbacks(o, callbacks, args) {
  function useStrictEquality (line 757) | function useStrictEquality(b, a) {
  function quote (line 908) | function quote( str ) {
  function literal (line 911) | function literal( o ) {
  function join (line 914) | function join( pre, arr, post ) {
  function array (line 924) | function array( arr ) {

FILE: jquery.jqote2.js
  function raise (line 25) | function raise(error, ext) {
  function dotted_ns (line 29) | function dotted_ns(fn) {
  function lambda (line 41) | function lambda(tmpl, t) {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (193K chars).
[
  {
    "path": "MIT-LICENSE.txt",
    "chars": 1068,
    "preview": "Copyright (c) 2010 aefxx, http://aefxx.com/\n\nPermission is hereby granted, free of charge, to any person obtaining\na cop"
  },
  {
    "path": "README",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "WTFPL-LICENSE.txt",
    "chars": 499,
    "preview": "          DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                   Version 2, December 2004\n\n Copyright (C) 2004 S"
  },
  {
    "path": "external/ejs_production.js",
    "chars": 12881,
    "preview": "(function(){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_"
  },
  {
    "path": "external/jquery.benchmark.js",
    "chars": 462,
    "preview": "// based on methodology developed by PPK:\n// http://www.quirksmode.org/blog/archives/2009/08/when_to_read_ou.html\n(funct"
  },
  {
    "path": "external/jquery.mustache.js",
    "chars": 9225,
    "preview": "/*\nShameless port of a shameless port\n@defunkt => @janl => @aq\n \nSee http://github.com/defunkt/mustache for more info.\n*"
  },
  {
    "path": "external/jquery.nano.js",
    "chars": 381,
    "preview": "/* Nano Templates (Tomasz Mazur, Jacek Becela) */\n\n(function($){\n  $.nano = function(template, data) {\n    return templa"
  },
  {
    "path": "external/jquery.srender.js",
    "chars": 1399,
    "preview": "// Simple JavaScript Templating\n// John Resig - http://ejohn.org/ - MIT Licensed\n// adapted from: http://ejohn.org/blog/"
  },
  {
    "path": "external/jquery.tempest.js",
    "chars": 20985,
    "preview": "// Tempest jQuery Templating Plugin\n// ================================\n//\n// Copyright (c) 2009, 2010 Nick Fitzgerald -"
  },
  {
    "path": "external/jquery.templates.js",
    "chars": 2741,
    "preview": "$.templates = {};\n// wycats' templating plugin\n// (c) Yehuda Katz\n// You may distribute this code under the same license"
  },
  {
    "path": "external/jquery.tmpl.js",
    "chars": 18839,
    "preview": "/*\n * jQuery Templating Plugin\n * Copyright 2010, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n "
  },
  {
    "path": "external/pure.js",
    "chars": 21338,
    "preview": "/*!\n\tPURE Unobtrusive Rendering Engine for HTML\n\n\tLicensed under the MIT licenses.\n\tMore information at: http://www.open"
  },
  {
    "path": "external/qunit.css",
    "chars": 2947,
    "preview": "\nol#qunit-tests {\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Aria"
  },
  {
    "path": "external/qunit.js",
    "chars": 29041,
    "preview": "/*\n * QUnit - A JavaScript Unit Testing Framework\n * \n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2009 John Resi"
  },
  {
    "path": "external/styles.css",
    "chars": 2578,
    "preview": "html, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, bi"
  },
  {
    "path": "external/underscore.js",
    "chars": 25103,
    "preview": "//     (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.\n//     Underscore is freely distributable under the MIT license.\n// "
  },
  {
    "path": "jqote.benchmark.htm",
    "chars": 18576,
    "preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n    <head"
  },
  {
    "path": "jqote.qunit.htm",
    "chars": 4694,
    "preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n    <head>"
  },
  {
    "path": "jquery.jqote2.js",
    "chars": 6574,
    "preview": "/*\n * jQote2 - client-side Javascript templating engine\n * Copyright (C) 2010, aefxx\n * http://aefxx.com/\n *\n * Dual lic"
  },
  {
    "path": "version.txt",
    "chars": 6,
    "preview": "0.9.8\n"
  }
]

About this extraction

This page contains the full source code of the aefxx/jQote2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (175.1 KB), approximately 47.1k tokens, and a symbol index with 77 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!