]> www.infradead.org Git - users/mchehab/andors-trail.git/commitdiff
Added missing file to last checkin
authorOskar Wiksten <oskar.wiksten@gmail.com>
Tue, 10 Jul 2012 06:41:49 +0000 (08:41 +0200)
committerOskar Wiksten <oskar.wiksten@gmail.com>
Tue, 10 Jul 2012 17:30:15 +0000 (19:30 +0200)
AndorsTrailEdit/inc/mustache.js [new file with mode: 0644]

diff --git a/AndorsTrailEdit/inc/mustache.js b/AndorsTrailEdit/inc/mustache.js
new file mode 100644 (file)
index 0000000..085d60b
--- /dev/null
@@ -0,0 +1,597 @@
+/*!
+ * 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.1-dev";
+  exports.tags = ["{{", "}}"];
+
+  exports.parse = parse;
+  exports.clearCache = clearCache;
+  exports.compile = compile;
+  exports.compilePartial = compilePartial;
+  exports.render = render;
+
+  exports.Scanner = Scanner;
+  exports.Context = Context;
+  exports.Renderer = Renderer;
+
+  // 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 whiteRe = /\s*/;
+  var spaceRe = /\s+/;
+  var nonSpaceRe = /\S/;
+  var eqRe = /\s*=/;
+  var curlyRe = /\s*\}/;
+  var tagRe = /#|\^|\/|>|\{|&|=|!/;
+
+  // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
+  // See https://github.com/janl/mustache.js/issues/189
+  function testRe(re, string) {
+    return RegExp.prototype.test.call(re, string);
+  }
+
+  function isWhitespace(string) {
+    return !testRe(nonSpaceRe, string);
+  }
+
+  var isArray = Array.isArray || function (obj) {
+    return Object.prototype.toString.call(obj) === "[object Array]";
+  };
+
+  // OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
+  var jsCharsRe = /[\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\xFF\u2028\u2029]/gm;
+
+  function quote(text) {
+    var escaped = text.replace(jsCharsRe, function (c) {
+      return "\\u" + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
+    });
+
+    return '"' + escaped + '"';
+  }
+
+  function escapeRe(string) {
+    return string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+  }
+
+  var entityMap = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': '&quot;',
+    "'": '&#39;',
+    "/": '&#x2F;'
+  };
+
+  function escapeHtml(string) {
+    return String(string).replace(/[&<>"'\/]/g, function (s) {
+      return entityMap[s];
+    });
+  }
+
+  // Export these utility functions.
+  exports.isWhitespace = isWhitespace;
+  exports.isArray = isArray;
+  exports.quote = quote;
+  exports.escapeRe = escapeRe;
+  exports.escapeHtml = escapeHtml;
+
+  function Scanner(string) {
+    this.string = string;
+    this.tail = string;
+    this.pos = 0;
+  }
+
+  /**
+   * Returns `true` if the tail is empty (end of string).
+   */
+  Scanner.prototype.eos = function () {
+    return this.tail === "";
+  };
+
+  /**
+   * Tries to match the given regular expression at the current position.
+   * Returns the matched text if it can match, `null` otherwise.
+   */
+  Scanner.prototype.scan = function (re) {
+    var match = this.tail.match(re);
+
+    if (match && match.index === 0) {
+      this.tail = this.tail.substring(match[0].length);
+      this.pos += match[0].length;
+      return match[0];
+    }
+
+    return null;
+  };
+
+  /**
+   * Skips all text until the given regular expression can be matched. Returns
+   * the skipped string, which is the entire tail of this scanner if no match
+   * can be made.
+   */
+  Scanner.prototype.scanUntil = function (re) {
+    var match, pos = this.tail.search(re);
+
+    switch (pos) {
+    case -1:
+      match = this.tail;
+      this.pos += this.tail.length;
+      this.tail = "";
+      break;
+    case 0:
+      match = null;
+      break;
+    default:
+      match = this.tail.substring(0, pos);
+      this.tail = this.tail.substring(pos);
+      this.pos += pos;
+    }
+
+    return match;
+  };
+
+  function Context(view, parent) {
+    this.view = view;
+    this.parent = parent;
+    this.clearCache();
+  }
+
+  Context.make = function (view) {
+    return (view instanceof Context) ? view : new Context(view);
+  };
+
+  Context.prototype.clearCache = function () {
+    this._cache = {};
+  };
+
+  Context.prototype.push = function (view) {
+    return new Context(view, this);
+  };
+
+  Context.prototype.lookup = function (name) {
+    var value = this._cache[name];
+
+    if (!value) {
+      if (name === ".") {
+        value = this.view;
+      } else {
+        var context = this;
+
+        while (context) {
+          if (name.indexOf(".") > 0) {
+            var names = name.split("."), i = 0;
+
+            value = context.view;
+
+            while (value && i < names.length) {
+              value = value[names[i++]];
+            }
+          } else {
+            value = context.view[name];
+          }
+
+          if (value != null) {
+            break;
+          }
+
+          context = context.parent;
+        }
+      }
+
+      this._cache[name] = value;
+    }
+
+    if (typeof value === "function") {
+      value = value.call(this.view);
+    }
+
+    return value;
+  };
+
+  function Renderer() {
+    this.clearCache();
+  }
+
+  Renderer.prototype.clearCache = function () {
+    this._cache = {};
+    this._partialCache = {};
+  };
+
+  Renderer.prototype.compile = function (tokens, tags) {
+    var fn = compileTokens(tokens),
+        self = this;
+
+    return function (view) {
+      return fn(Context.make(view), self);
+    };
+  };
+
+  Renderer.prototype.compilePartial = function (name, tokens, tags) {
+    this._partialCache[name] = this.compile(tokens, tags);
+    return this._partialCache[name];
+  };
+
+  Renderer.prototype.render = function (template, view) {
+    var fn = this._cache[template];
+
+    if (!fn) {
+      fn = this.compile(template);
+      this._cache[template] = fn;
+    }
+
+    return fn(view);
+  };
+
+  Renderer.prototype._section = function (name, context, callback) {
+    var value = context.lookup(name);
+
+    switch (typeof value) {
+    case "object":
+      if (isArray(value)) {
+        var buffer = "";
+        for (var i = 0, len = value.length; i < len; ++i) {
+          buffer += callback(context.push(value[i]), this);
+        }
+        return buffer;
+      } else {
+        return callback(context.push(value), this);
+      }
+      break;
+    case "function":
+      var sectionText = callback(context, this), self = this;
+      var scopedRender = function (template) {
+        return self.render(template, context);
+      };
+      return value.call(context.view, sectionText, scopedRender) || "";
+      break;
+    default:
+      if (value) {
+        return callback(context, this);
+      }
+    }
+
+    return "";
+  };
+
+  Renderer.prototype._inverted = function (name, context, callback) {
+    var value = context.lookup(name);
+
+    // 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)) {
+      return callback(context, this);
+    }
+
+    return "";
+  };
+
+  Renderer.prototype._partial = function (name, context) {
+    var fn = this._partialCache[name];
+
+    if (fn) {
+      return fn(context, this);
+    }
+
+    return "";
+  };
+
+  Renderer.prototype._name = function (name, context, escape) {
+    var value = context.lookup(name);
+
+    if (typeof value === "function") {
+      value = value.call(context.view);
+    }
+
+    var string = (value == null) ? "" : String(value);
+
+    if (escape) {
+      return escapeHtml(string);
+    }
+
+    return string;
+  };
+
+  /**
+   * Low-level function that compiles the given `tokens` into a
+   * function that accepts two arguments: a Context and a
+   * Renderer. Returns the body of the function as a string if
+   * `returnBody` is true.
+   */
+  function compileTokens(tokens, returnBody) {
+    if (typeof tokens === "string") {
+      tokens = parse(tokens);
+    }
+
+    var body = ['""'];
+    var token, method, escape;
+
+    for (var i = 0, len = tokens.length; i < len; ++i) {
+      token = tokens[i];
+
+      switch (token.type) {
+      case "#":
+      case "^":
+        method = (token.type === "#") ? "_section" : "_inverted";
+        body.push("r." + method + "(" + quote(token.value) + ", c, function (c, r) {\n" +
+          "  " + compileTokens(token.tokens, true) + "\n" +
+          "})");
+        break;
+      case "{":
+      case "&":
+      case "name":
+        escape = token.type === "name" ? "true" : "false";
+        body.push("r._name(" + quote(token.value) + ", c, " + escape + ")");
+        break;
+      case ">":
+        body.push("r._partial(" + quote(token.value) + ", c)");
+        break;
+      case "text":
+        body.push(quote(token.value));
+        break;
+      }
+    }
+
+    // Convert to a string body.
+    body = "return " + body.join(" + ") + ";";
+
+    // Good for debugging.
+    // console.log(body);
+
+    if (returnBody) {
+      return body;
+    }
+
+    // For great evil!
+    return new Function("c, r", body);
+  }
+
+  function escapeTags(tags) {
+    if (tags.length === 2) {
+      return [
+        new RegExp(escapeRe(tags[0]) + "\\s*"),
+        new RegExp("\\s*" + escapeRe(tags[1]))
+      ];
+    }
+
+    throw new Error("Invalid tags: " + tags.join(" "));
+  }
+
+  /**
+   * Forms the given linear array of `tokens` into a nested tree structure
+   * where tokens that represent a section have a "tokens" array property
+   * that contains all tokens that are in that section.
+   */
+  function nestTokens(tokens) {
+    var tree = [];
+    var collector = tree;
+    var sections = [];
+    var token, section;
+
+    for (var i = 0; i < tokens.length; ++i) {
+      token = tokens[i];
+
+      switch (token.type) {
+      case "#":
+      case "^":
+        token.tokens = [];
+        sections.push(token);
+        collector.push(token);
+        collector = token.tokens;
+        break;
+      case "/":
+        if (sections.length === 0) {
+          throw new Error("Unopened section: " + token.value);
+        }
+
+        section = sections.pop();
+
+        if (section.value !== token.value) {
+          throw new Error("Unclosed section: " + section.value);
+        }
+
+        if (sections.length > 0) {
+          collector = sections[sections.length - 1].tokens;
+        } else {
+          collector = tree;
+        }
+        break;
+      default:
+        collector.push(token);
+      }
+    }
+
+    // Make sure there were no open sections when we're done.
+    section = sections.pop();
+
+    if (section) {
+      throw new Error("Unclosed section: " + section.value);
+    }
+
+    return tree;
+  }
+
+  /**
+   * Combines the values of consecutive text tokens in the given `tokens` array
+   * to a single token.
+   */
+  function squashTokens(tokens) {
+    var lastToken;
+
+    for (var i = 0; i < tokens.length; ++i) {
+      var token = tokens[i];
+
+      if (lastToken && lastToken.type === "text" && token.type === "text") {
+        lastToken.value += token.value;
+        tokens.splice(i--, 1); // Remove this token from the array.
+      } else {
+        lastToken = token;
+      }
+    }
+  }
+
+  /**
+   * Breaks up the given `template` string into a tree of token objects. If
+   * `tags` is given here it must be an array with two string values: the
+   * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
+   * course, the default is to use mustaches (i.e. Mustache.tags).
+   */
+  function parse(template, tags) {
+    tags = tags || exports.tags;
+    var tagRes = escapeTags(tags);
+
+    var scanner = new Scanner(template);
+
+    var tokens = [],      // Buffer to hold the tokens
+        spaces = [],      // Indices of whitespace tokens 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 whitespace tokens array for the current line
+    // if there was a {{#tag}} on it and otherwise only space.
+    var stripSpace = function () {
+      if (hasTag && !nonSpace) {
+        while (spaces.length) {
+          tokens.splice(spaces.pop(), 1);
+        }
+      } else {
+        spaces = [];
+      }
+
+      hasTag = false;
+      nonSpace = false;
+    };
+
+    var type, value, chr;
+
+    while (!scanner.eos()) {
+      value = scanner.scanUntil(tagRes[0]);
+
+      if (value) {
+        for (var i = 0, len = value.length; i < len; ++i) {
+          chr = value[i];
+
+          if (isWhitespace(chr)) {
+            spaces.push(tokens.length);
+          } else {
+            nonSpace = true;
+          }
+
+          tokens.push({type: "text", value: chr});
+
+          if (chr === "\n") {
+            stripSpace(); // Check for whitespace on the current line.
+          }
+        }
+      }
+
+      // Match the opening tag.
+      if (!scanner.scan(tagRes[0])) {
+        break;
+      }
+
+      hasTag = true;
+      type = scanner.scan(tagRe) || "name";
+
+      // Skip any whitespace between tag and value.
+      scanner.scan(whiteRe);
+
+      // Extract the tag value.
+      if (type === "=") {
+        value = scanner.scanUntil(eqRe);
+        scanner.scan(eqRe);
+        scanner.scanUntil(tagRes[1]);
+      } else if (type === "{") {
+        var closeRe = new RegExp("\\s*" + escapeRe("}" + tags[1]));
+        value = scanner.scanUntil(closeRe);
+        scanner.scan(curlyRe);
+        scanner.scanUntil(tagRes[1]);
+      } else {
+        value = scanner.scanUntil(tagRes[1]);
+      }
+
+      // Match the closing tag.
+      if (!scanner.scan(tagRes[1])) {
+        throw new Error("Unclosed tag at " + scanner.pos);
+      }
+
+      tokens.push({type: type, value: value});
+
+      if (type === "name" || type === "{" || type === "&") {
+        nonSpace = true;
+      }
+
+      // Set the tags for the next time around.
+      if (type === "=") {
+        tags = value.split(spaceRe);
+        tagRes = escapeTags(tags);
+      }
+    }
+
+    squashTokens(tokens);
+
+    return nestTokens(tokens);
+  }
+
+  // The high-level clearCache, compile, compilePartial, and render functions
+  // use this default renderer.
+  var _renderer = new Renderer;
+
+  /**
+   * Clears all cached templates and partials.
+   */
+  function clearCache() {
+    _renderer.clearCache();
+  }
+
+  /**
+   * High-level API for compiling the given `tokens` down to a reusable
+   * function. If `tokens` is a string it will be parsed using the given `tags`
+   * before it is compiled.
+   */
+  function compile(tokens, tags) {
+    return _renderer.compile(tokens, tags);
+  }
+
+  /**
+   * High-level API for compiling the `tokens` for the partial with the given
+   * `name` down to a reusable function. If `tokens` is a string it will be
+   * parsed using the given `tags` before it is compiled.
+   */
+  function compilePartial(name, tokens, tags) {
+    return _renderer.compilePartial(name, tokens, tags);
+  }
+
+  /**
+   * High-level API for rendering the `template` using the given `view`. The
+   * optional `partials` object may be given here for convenience, but note that
+   * it will cause all partials to be re-compiled, thus hurting performance. Of
+   * course, this only matters if you're going to render the same template more
+   * than once. If so, it is best to call `compilePartial` before calling this
+   * function and to leave the `partials` argument blank.
+   */
+  function render(template, view, partials) {
+    if (partials) {
+      for (var name in partials) {
+        compilePartial(name, partials[name]);
+      }
+    }
+
+    return _renderer.render(template, view);
+  }
+
+})(Mustache);