User:McIntireEvan/afch-rewrite.js/hogan.js

/* Uploaded from https://github.com/WPAFC/afch-rewrite, commit: 5059ac05b812b07ffbf8e8b8112d049813cad9bd (master) */ /* * Copyright 2011 Twitter, Inc. *  Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */

var Hogan = {};

(function (Hogan, useArrayBuffer) { Hogan.Template = function (renderFunc, text, compiler, options) {    this.r = renderFunc || this.r;    this.c = compiler;    this.options = options;    this.text = text || ;    this.buf = (useArrayBuffer) ? [] : ;  }

Hogan.Template.prototype = { // render: replaced by generated code. r: function (context, partials, indent) { return ''; },

// variable escaping v: hoganEscape,

// triple stache t: coerceToString,

render: function render(context, partials, indent) { return this.ri([context], partials || {}, indent); },

// render internal -- a hook for overrides that catches partials too ri: function (context, partials, indent) { return this.r(context, partials, indent); },

// tries to find a partial in the curent scope and render it   rp: function(name, context, partials, indent) { var partial = partials[name];

if (!partial) { return ''; }

if (this.c && typeof partial == 'string') { partial = this.c.compile(partial, this.options); }

return partial.ri(context, partials, indent); },

// render a section rs: function(context, partials, section) { var tail = context[context.length - 1];

if (!isArray(tail)) { section(context, partials, this); return; }

for (var i = 0; i < tail.length; i++) { context.push(tail[i]); section(context, partials, this); context.pop; }   },

// maybe start a section s: function(val, ctx, partials, inverted, start, end, tags) { var pass;

if (isArray(val) && val.length === 0) { return false; }

if (typeof val == 'function') { val = this.ls(val, ctx, partials, inverted, start, end, tags); }

pass = (val === '') || !!val;

if (!inverted && pass && ctx) { ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]); }

return pass; },

// find values with dotted names d: function(key, ctx, partials, returnFound) { var names = key.split('.'), val = this.f(names[0], ctx, partials, returnFound), cx = null;

if (key === '.' && isArray(ctx[ctx.length - 2])) { return ctx[ctx.length - 1]; }

for (var i = 1; i < names.length; i++) { if (val && typeof val == 'object' && names[i] in val) { cx = val; val = val[names[i]]; } else { val = ''; }     }

if (returnFound && !val) { return false; }

if (!returnFound && typeof val == 'function') { ctx.push(cx); val = this.lv(val, ctx, partials); ctx.pop; }

return val; },

// find values with normal names f: function(key, ctx, partials, returnFound) { var val = false, v = null, found = false;

for (var i = ctx.length - 1; i >= 0; i--) { v = ctx[i]; if (v && typeof v == 'object' && key in v) { val = v[key]; found = true; break; }     }

if (!found) { return (returnFound) ? false : ""; }

if (!returnFound && typeof val == 'function') { val = this.lv(val, ctx, partials); }

return val; },

// higher order templates ho: function(val, cx, partials, text, tags) { var compiler = this.c;     var options = this.options; options.delimiters = tags; var text = val.call(cx, text); text = (text == null) ? String(text) : text.toString; this.b(compiler.compile(text, options).render(cx, partials)); return false; },

// template result buffering b: (useArrayBuffer) ? function(s) { this.buf.push(s); } : function(s) { this.buf += s; }, fl: (useArrayBuffer) ? function { var r = this.buf.join(''); this.buf = []; return r; } : function { var r = this.buf; this.buf = ''; return r; },

// lambda replace section ls: function(val, ctx, partials, inverted, start, end, tags) { var cx = ctx[ctx.length - 1], t = null;

if (!inverted && this.c && val.length > 0) { return this.ho(val, cx, partials, this.text.substring(start, end), tags); }

t = val.call(cx);

if (typeof t == 'function') { if (inverted) { return true; } else if (this.c) { return this.ho(t, cx, partials, this.text.substring(start, end), tags); }     }

return t;   },

// lambda replace variable lv: function(val, ctx, partials) { var cx = ctx[ctx.length - 1]; var result = val.call(cx);

if (typeof result == 'function') { result = coerceToString(result.call(cx)); if (this.c && ~result.indexOf("{\u007B")) { return this.c.compile(result, this.options).render(cx, partials); }     }

return coerceToString(result); }

};

var rAmp = /&/g, rLt = //g, rApos =/\'/g, rQuot = /\"/g,     hChars =/[&<>\"\']/;

function coerceToString(val) { return String((val === null || val === undefined) ? '' : val); }

function hoganEscape(str) { str = coerceToString(str); return hChars.test(str) ? str .replace(rAmp,'&amp;') .replace(rLt,'&lt;') .replace(rGt,'&gt;') .replace(rApos,'&#39;') .replace(rQuot, '&quot;') : str; }

var isArray = Array.isArray || function(a) { return Object.prototype.toString.call(a) === '[object Array]'; };

})(typeof exports !== 'undefined' ? exports : Hogan);

(function (Hogan) { // Setup regex  assignments  // remove whitespace according to Mustache spec  var rIsWhitespace = /\S/,      rQuot = /\"/g,      rNewline =  /\n/g,      rCr = /\r/g,      rSlash = /\\/g,      tagTypes = {        '#': 1, '^': 2, '/': 3,  '!': 4, '>': 5,        '<': 6, '=': 7, '_v': 8, '{': 9, '&': 10      };

Hogan.scan = function scan(text, delimiters) { var len = text.length, IN_TEXT = 0, IN_TAG_TYPE = 1, IN_TAG = 2, state = IN_TEXT, tagType = null, tag = null, buf = '', tokens = [], seenTag = false, i = 0, lineStart = 0, otag = '';

function addBuf { if (buf.length > 0) { tokens.push(new String(buf)); buf = ''; }   }

function lineIsWhitespace { var isAllWhitespace = true; for (var j = lineStart; j < tokens.length; j++) { isAllWhitespace = (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) || (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null); if (!isAllWhitespace) { return false; }     }

return isAllWhitespace; }

function filterLine(haveSeenTag, noNewLine) { addBuf;

if (haveSeenTag && lineIsWhitespace) { for (var j = lineStart, next; j < tokens.length; j++) { if (!tokens[j].tag) { if ((next = tokens[j+1]) && next.tag == '>') { // set indent to token value next.indent = tokens[j].toString }           tokens.splice(j, 1); }       }      } else if (!noNewLine) { tokens.push({tag:'\n'}); }

seenTag = false; lineStart = tokens.length; }

function changeDelimiters(text, index) { var close = '=' + ctag, closeIndex = text.indexOf(close, index), delimiters = trim(           text.substring(text.indexOf('=', index) + 1, closeIndex)          ).split(' ');

otag = delimiters[0]; ctag = delimiters[1];

return closeIndex + close.length - 1; }

if (delimiters) { delimiters = delimiters.split(' '); otag = delimiters[0]; ctag = delimiters[1]; }

for (i = 0; i < len; i++) { if (state == IN_TEXT) { if (tagChange(otag, text, i)) { --i; addBuf; state = IN_TAG_TYPE; } else { if (text.charAt(i) == '\n') { filterLine(seenTag); } else { buf += text.charAt(i); }       }      } else if (state == IN_TAG_TYPE) { i += otag.length - 1; tag = tagTypes[text.charAt(i + 1)]; tagType = tag ? text.charAt(i + 1) : '_v'; if (tagType == '=') { i = changeDelimiters(text, i); state = IN_TEXT; } else { if (tag) { i++; }         state = IN_TAG; }       seenTag = i;      } else { if (tagChange(ctag, text, i)) { tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,                      i: (tagType == '/') ? seenTag - ctag.length : i + otag.length}); buf = ''; i += ctag.length - 1; state = IN_TEXT; if (tagType == '{') { if (ctag == '}}') { i++; } else { cleanTripleStache(tokens[tokens.length - 1]); }         }        } else { buf += text.charAt(i); }     }    }

filterLine(seenTag, true);

return tokens; }

function cleanTripleStache(token) { if (token.n.substr(token.n.length - 1) === '}') { token.n = token.n.substring(0, token.n.length - 1); } }

function trim(s) { if (s.trim) { return s.trim; }

return s.replace(/^\s*|\s*$/g, ''); }

function tagChange(tag, text, index) { if (text.charAt(index) != tag.charAt(0)) { return false; }

for (var i = 1, l = tag.length; i < l; i++) { if (text.charAt(index + i) != tag.charAt(i)) { return false; }   }

return true; }

function buildTree(tokens, kind, stack, customTags) { var instructions = [], opener = null, token = null;

while (tokens.length > 0) { token = tokens.shift; if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) { stack.push(token); token.nodes = buildTree(tokens, token.tag, stack, customTags); instructions.push(token); } else if (token.tag == '/') { if (stack.length === 0) { throw new Error('Closing tag without opener: /' + token.n); }       opener = stack.pop; if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) { throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n); }       opener.end = token.i;        return instructions; } else { instructions.push(token); }   }

if (stack.length > 0) { throw new Error('missing closing tag: ' + stack.pop.n); }

return instructions; }

function isOpener(token, tags) { for (var i = 0, l = tags.length; i < l; i++) { if (tags[i].o == token.n) { token.tag = '#'; return true; }   }  }

function isCloser(close, open, tags) { for (var i = 0, l = tags.length; i < l; i++) { if (tags[i].c == close && tags[i].o == open) { return true; }   }  }

Hogan.generate = function (tree, text, options) { var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl;'; if (options.asString) { return 'function(c,p,i){' + code + ';}'; }

return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options); }

function esc(s) { return s.replace(rSlash, '\\\\') .replace(rQuot, '\\\"')           .replace(rNewline, '\\n')            .replace(rCr, '\\r');  }

function chooseMethod(s) { return (~s.indexOf('.')) ? 'd' : 'f'; }

function walk(tree) { var code = ''; for (var i = 0, l = tree.length; i < l; i++) { var tag = tree[i].tag; if (tag == '#') { code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),                       tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag); } else if (tag == '^') { code += invertedSection(tree[i].nodes, tree[i].n,                               chooseMethod(tree[i].n)); } else if (tag == '<' || tag == '>') { code += partial(tree[i]); } else if (tag == '{' || tag == '&') { code += tripleStache(tree[i].n, chooseMethod(tree[i].n)); } else if (tag == '\n') { code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i')); } else if (tag == '_v') { code += variable(tree[i].n, chooseMethod(tree[i].n)); } else if (tag === undefined) { code += text('"' + esc(tree[i]) + '"'); }   }    return code; }

function section(nodes, id, method, start, end, tags) { return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' + 'c,p,0,' + start + ',' + end + ',"' + tags + '")){' + '_.rs(c,p,' +          'function(c,p,_){' +           walk(nodes) +           '});c.pop;}'; }

function invertedSection(nodes, id, method) { return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' + walk(nodes) + '};'; }

function partial(tok) { return '_.b(_.rp("' + esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));'; }

function tripleStache(id, method) { return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));'; }

function variable(id, method) { return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));'; }

function text(id) { return '_.b(' + id + ');'; }

Hogan.parse = function(tokens, text, options) { options = options || {}; return buildTree(tokens, '', [], options.sectionTags || []); },

Hogan.cache = {};

Hogan.compile = function(text, options) { // options //   // asString: false (default) //   // sectionTags: [{o: '_foo', c: 'foo'}] // An array of object with o and c fields that indicate names for custom // section tags. The example above allows parsing of. //   // delimiters: A string that overrides the default delimiters. // Example: "<% %>" //   options = options || {};

var key = text + '||' + !!options.asString;

var t = this.cache[key];

if (t) { return t;   }

t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options); return this.cache[key] = t; }; })(typeof exports !== 'undefined' ? exports : Hogan);