| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 | /*! * mustache.js - Logic-less {{mustache}} templates with JavaScript * http://github.com/janl/mustache.js */var Mustache = (typeof module !== "undefined" && module.exports) || {};(function (exports) {  exports.name = "mustache.js";  exports.version = "0.5.0-dev";  exports.tags = ["{{", "}}"];  exports.parse = parse;  exports.compile = compile;  exports.render = render;  exports.clearCache = clearCache;  // This is here for backwards compatibility with 0.4.x.  exports.to_html = function (template, view, partials, send) {    var result = render(template, view, partials);    if (typeof send === "function") {      send(result);    } else {      return result;    }  };  var _toString = Object.prototype.toString;  var _isArray = Array.isArray;  var _forEach = Array.prototype.forEach;  var _trim = String.prototype.trim;  var isArray;  if (_isArray) {    isArray = _isArray;  } else {    isArray = function (obj) {      return _toString.call(obj) === "[object Array]";    };  }  var forEach;  if (_forEach) {    forEach = function (obj, callback, scope) {      return _forEach.call(obj, callback, scope);    };  } else {    forEach = function (obj, callback, scope) {      for (var i = 0, len = obj.length; i < len; ++i) {        callback.call(scope, obj[i], i, obj);      }    };  }  var spaceRe = /^\s*$/;  function isWhitespace(string) {    return spaceRe.test(string);  }  var trim;  if (_trim) {    trim = function (string) {      return string == null ? "" : _trim.call(string);    };  } else {    var trimLeft, trimRight;    if (isWhitespace("\xA0")) {      trimLeft = /^\s+/;      trimRight = /\s+$/;    } else {      // IE doesn't match non-breaking spaces with \s, thanks jQuery.      trimLeft = /^[\s\xA0]+/;      trimRight = /[\s\xA0]+$/;    }    trim = function (string) {      return string == null ? "" :        String(string).replace(trimLeft, "").replace(trimRight, "");    };  }  var escapeMap = {    "&": "&",    "<": "<",    ">": ">",    '"': '"',    "'": '''  };  function escapeHTML(string) {    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {      return escapeMap[s] || s;    });  }  /**   * Adds the `template`, `line`, and `file` properties to the given error   * object and alters the message to provide more useful debugging information.   */  function debug(e, template, line, file) {    file = file || "<template>";    var lines = template.split("\n"),        start = Math.max(line - 3, 0),        end = Math.min(lines.length, line + 3),        context = lines.slice(start, end);    var c;    for (var i = 0, len = context.length; i < len; ++i) {      c = i + start + 1;      context[i] = (c === line ? " >> " : "    ") + context[i];    }    e.template = template;    e.line = line;    e.file = file;    e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");    return e;  }  /**   * Looks up the value of the given `name` in the given context `stack`.   */  function lookup(name, stack, defaultValue) {    if (name === ".") {      return stack[stack.length - 1];    }    var names = name.split(".");    var lastIndex = names.length - 1;    var target = names[lastIndex];    var value, context, i = stack.length, j, localStack;    while (i) {      localStack = stack.slice(0);      context = stack[--i];      j = 0;      while (j < lastIndex) {        context = context[names[j++]];        if (context == null) {          break;        }        localStack.push(context);      }      if (context && typeof context === "object" && target in context) {        value = context[target];        break;      }    }    // If the value is a function, call it in the current context.    if (typeof value === "function") {      value = value.call(localStack[localStack.length - 1]);    }    if (value == null)  {      return defaultValue;    }    return value;  }  function renderSection(name, stack, callback, inverted) {    var buffer = "";    var value =  lookup(name, stack);    if (inverted) {      // From the spec: inverted sections may render text once based on the      // inverse value of the key. That is, they will be rendered if the key      // doesn't exist, is false, or is an empty list.      if (value == null || value === false || (isArray(value) && value.length === 0)) {        buffer += callback();      }    } else if (isArray(value)) {      forEach(value, function (value) {        stack.push(value);        buffer += callback();        stack.pop();      });    } else if (typeof value === "object") {      stack.push(value);      buffer += callback();      stack.pop();    } else if (typeof value === "function") {      var scope = stack[stack.length - 1];      var scopedRender = function (template) {        return render(template, scope);      };      buffer += value.call(scope, callback(), scopedRender) || "";    } else if (value) {      buffer += callback();    }    return buffer;  }  /**   * Parses the given `template` and returns the source of a function that,   * with the proper arguments, will render the template. Recognized options   * include the following:   *   *   - file     The name of the file the template comes from (displayed in   *              error messages)   *   - tags     An array of open and close tags the `template` uses. Defaults   *              to the value of Mustache.tags   *   - debug    Set `true` to log the body of the generated function to the   *              console   *   - space    Set `true` to preserve whitespace from lines that otherwise   *              contain only a {{tag}}. Defaults to `false`   */  function parse(template, options) {    options = options || {};    var tags = options.tags || exports.tags,        openTag = tags[0],        closeTag = tags[tags.length - 1];    var code = [      'var buffer = "";', // output buffer      "\nvar line = 1;", // keep track of source line number      "\ntry {",      '\nbuffer += "'    ];    var spaces = [],      // indices of whitespace in code on the current line        hasTag = false,   // is there a {{tag}} on the current line?        nonSpace = false; // is there a non-space char on the current line?    // Strips all space characters from the code array for the current line    // if there was a {{tag}} on it and otherwise only spaces.    var stripSpace = function () {      if (hasTag && !nonSpace && !options.space) {        while (spaces.length) {          code.splice(spaces.pop(), 1);        }      } else {        spaces = [];      }      hasTag = false;      nonSpace = false;    };    var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;    var setTags = function (source) {      tags = trim(source).split(/\s+/);      nextOpenTag = tags[0];      nextCloseTag = tags[tags.length - 1];    };    var includePartial = function (source) {      code.push(        '";',        updateLine,        '\nvar partial = partials["' + trim(source) + '"];',        '\nif (partial) {',        '\n  buffer += render(partial,stack[stack.length - 1],partials);',        '\n}',        '\nbuffer += "'      );    };    var openSection = function (source, inverted) {      var name = trim(source);      if (name === "") {        throw debug(new Error("Section name may not be empty"), template, line, options.file);      }      sectionStack.push({name: name, inverted: inverted});      code.push(        '";',        updateLine,        '\nvar name = "' + name + '";',        '\nvar callback = (function () {',        '\n  return function () {',        '\n    var buffer = "";',        '\nbuffer += "'      );    };    var openInvertedSection = function (source) {      openSection(source, true);    };    var closeSection = function (source) {      var name = trim(source);      var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;      if (!openName || name != openName) {        throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);      }      var section = sectionStack.pop();      code.push(        '";',        '\n    return buffer;',        '\n  };',        '\n})();'      );      if (section.inverted) {        code.push("\nbuffer += renderSection(name,stack,callback,true);");      } else {        code.push("\nbuffer += renderSection(name,stack,callback);");      }      code.push('\nbuffer += "');    };    var sendPlain = function (source) {      code.push(        '";',        updateLine,        '\nbuffer += lookup("' + trim(source) + '",stack,"");',        '\nbuffer += "'      );    };    var sendEscaped = function (source) {      code.push(        '";',        updateLine,        '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',        '\nbuffer += "'      );    };    var line = 1, c, callback;    for (var i = 0, len = template.length; i < len; ++i) {      if (template.slice(i, i + openTag.length) === openTag) {        i += openTag.length;        c = template.substr(i, 1);        updateLine = '\nline = ' + line + ';';        nextOpenTag = openTag;        nextCloseTag = closeTag;        hasTag = true;        switch (c) {        case "!": // comment          i++;          callback = null;          break;        case "=": // change open/close tags, e.g. {{=<% %>=}}          i++;          closeTag = "=" + closeTag;          callback = setTags;          break;        case ">": // include partial          i++;          callback = includePartial;          break;        case "#": // start section          i++;          callback = openSection;          break;        case "^": // start inverted section          i++;          callback = openInvertedSection;          break;        case "/": // end section          i++;          callback = closeSection;          break;        case "{": // plain variable          closeTag = "}" + closeTag;          // fall through        case "&": // plain variable          i++;          nonSpace = true;          callback = sendPlain;          break;        default: // escaped variable          nonSpace = true;          callback = sendEscaped;        }        var end = template.indexOf(closeTag, i);        if (end === -1) {          throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);        }        var source = template.substring(i, end);        if (callback) {          callback(source);        }        // Maintain line count for \n in source.        var n = 0;        while (~(n = source.indexOf("\n", n))) {          line++;          n++;        }        i = end + closeTag.length - 1;        openTag = nextOpenTag;        closeTag = nextCloseTag;      } else {        c = template.substr(i, 1);        switch (c) {        case '"':        case "\\":          nonSpace = true;          code.push("\\" + c);          break;        case "\r":          // Ignore carriage returns.          break;        case "\n":          spaces.push(code.length);          code.push("\\n");          stripSpace(); // Check for whitespace on the current line.          line++;          break;        default:          if (isWhitespace(c)) {            spaces.push(code.length);          } else {            nonSpace = true;          }          code.push(c);        }      }    }    if (sectionStack.length != 0) {      throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);    }    // Clean up any whitespace from a closing {{tag}} that was at the end    // of the template without a trailing \n.    stripSpace();    code.push(      '";',      "\nreturn buffer;",      "\n} catch (e) { throw {error: e, line: line}; }"    );    // Ignore `buffer += "";` statements.    var body = code.join("").replace(/buffer \+= "";\n/g, "");    if (options.debug) {      if (typeof console != "undefined" && console.log) {        console.log(body);      } else if (typeof print === "function") {        print(body);      }    }    return body;  }  /**   * Used by `compile` to generate a reusable function for the given `template`.   */  function _compile(template, options) {    var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";    var body = parse(template, options);    var fn = new Function(args, body);    // This anonymous function wraps the generated function so we can do    // argument coercion, setup some variables, and handle any errors    // encountered while executing it.    return function (view, partials) {      partials = partials || {};      var stack = [view]; // context stack      try {        return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);      } catch (e) {        throw debug(e.error, template, e.line, options.file);      }    };  }  // Cache of pre-compiled templates.  var _cache = {};  /**   * Clear the cache of compiled templates.   */  function clearCache() {    _cache = {};  }  /**   * Compiles the given `template` into a reusable function using the given   * `options`. In addition to the options accepted by Mustache.parse,   * recognized options include the following:   *   *   - cache    Set `false` to bypass any pre-compiled version of the given   *              template. Otherwise, a given `template` string will be cached   *              the first time it is parsed   */  function compile(template, options) {    options = options || {};    // Use a pre-compiled version from the cache if we have one.    if (options.cache !== false) {      if (!_cache[template]) {        _cache[template] = _compile(template, options);      }      return _cache[template];    }    return _compile(template, options);  }  /**   * High-level function that renders the given `template` using the given   * `view` and `partials`. If you need to use any of the template options (see   * `compile` above), you must compile in a separate step, and then call that   * compiled function.   */  function render(template, view, partials) {    return compile(template)(view, partials);  }})(Mustache);
 |