Advertisement
Guest User

LoDash Template

a guest
Aug 14th, 2012
581
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*!
  2.  * Lo-Dash v0.5.0-rc.1 <http://lodash.com>
  3.  * Copyright 2012 John-David Dalton <http://allyoucanleet.com/>
  4.  * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
  5.  * <http://documentcloud.github.com/underscore>
  6.  * Available under MIT license <http://lodash.com/license>
  7.  */
  8. ;(function(window, undefined) {
  9.   'use strict';
  10.  
  11.   /**
  12.    * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid
  13.    * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp.
  14.    * Assigned in `_.template`.
  15.    */
  16.   var lastEvaluateDelimiter;
  17.  
  18.   /**
  19.    * Used to cache the last template `options.variable` to avoid unnecessarily
  20.    * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`.
  21.    */
  22.   var lastVariable;
  23.  
  24.   /**
  25.    * Used to match potentially incorrect data object references, like `obj.obj`,
  26.    * in compiled templates. Assigned in `_.template`.
  27.    */
  28.   var reDoubleVariable;
  29.  
  30.   /**
  31.    * Used to match "evaluate" delimiters, including internal delimiters,
  32.    * in template text. Assigned in `_.template`.
  33.    */
  34.   var reEvaluateDelimiter;
  35.  
  36.   /** Detect free variable `exports` */
  37.   var freeExports = typeof exports == 'object' && exports &&
  38.     (typeof global == 'object' && global && global == global.global && (window = global), exports);
  39.  
  40.   /** Native prototype shortcuts */
  41.   var ArrayProto = Array.prototype,
  42.       BoolProto = Boolean.prototype,
  43.       ObjectProto = Object.prototype,
  44.       NumberProto = Number.prototype,
  45.       StringProto = String.prototype;
  46.  
  47.   /** Used to generate unique IDs */
  48.   var idCounter = 0;
  49.  
  50.   /** Used to restore the original `_` reference in `noConflict` */
  51.   var oldDash = window._;
  52.  
  53.   /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */
  54.   var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/;
  55.  
  56.   /** Used to match empty string literals in compiled template source */
  57.   var reEmptyStringLeading = /\b__p \+= '';/g,
  58.       reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
  59.       reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
  60.  
  61.   /** Used to match regexp flags from their coerced string values */
  62.   var reFlags = /\w*$/;
  63.  
  64.   /** Used to insert the data object variable into compiled template source */
  65.   var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g;
  66.  
  67.   /** Used to match tokens in template text */
  68.   var reToken = /__token__(\d+)/g;
  69.  
  70.   /** Used to match unescaped characters in strings for inclusion in HTML */
  71.   var reUnescapedHtml = /[&<"']/g;
  72.  
  73.   /** Used to match unescaped characters in compiled string literals */
  74.   var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
  75.  
  76.   /** Used to fix the JScript [[DontEnum]] bug */
  77.   var shadowed = [
  78.     'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
  79.     'toLocaleString', 'toString', 'valueOf'
  80.   ];
  81.  
  82.   /** Used to make template sourceURLs easier to identify */
  83.   var templateCounter = 0;
  84.  
  85.   /** Used to replace template delimiters */
  86.   var token = '__token__';
  87.  
  88.   /** Used to store tokenized template text snippets */
  89.   var tokenized = [];
  90.  
  91.   /** Native method shortcuts */
  92.   var concat = ArrayProto.concat,
  93.       hasOwnProperty = ObjectProto.hasOwnProperty,
  94.       push = ArrayProto.push,
  95.       propertyIsEnumerable = ObjectProto.propertyIsEnumerable,
  96.       slice = ArrayProto.slice,
  97.       toString = ObjectProto.toString;
  98.  
  99.   /* Native method shortcuts for methods with the same name as other `lodash` methods */
  100.   var nativeIsFinite = window.isFinite;
  101.  
  102.   /** `Object#toString` result shortcuts */
  103.   var argsClass = '[object Arguments]',
  104.       arrayClass = '[object Array]',
  105.       boolClass = '[object Boolean]',
  106.       dateClass = '[object Date]',
  107.       funcClass = '[object Function]',
  108.       numberClass = '[object Number]',
  109.       objectClass = '[object Object]',
  110.       regexpClass = '[object RegExp]',
  111.       stringClass = '[object String]';
  112.  
  113.   /** Timer shortcuts */
  114.   var clearTimeout = window.clearTimeout,
  115.       setTimeout = window.setTimeout;
  116.  
  117.  
  118.  
  119.   /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */
  120.  
  121.  
  122.  
  123.  
  124.   /**
  125.    * Detect lack of support for accessing string characters by index:
  126.    * IE < 8 can't access characters by index and IE 8 can only access
  127.    * characters by index on string literals.
  128.    */
  129.   var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx';
  130.  
  131.   /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */
  132.   var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent);
  133.  
  134.   /** Detect if sourceURL syntax is usable without erroring */
  135.   try {
  136.     // The JS engine in Adobe products, like InDesign, will throw a syntax error
  137.     // when it encounters a single line comment beginning with the `@` symbol.
  138.     // The JS engine in Narwhal will generate the function `function anonymous(){//}`
  139.     // and throw a syntax error. In IE, `@` symbols are part of its non-standard
  140.     // conditional compilation support. The `@cc_on` statement activates its support
  141.     // while the trailing ` !` induces a syntax error to exlude it. Compatibility
  142.     // modes in IE > 8 require a space before the `!` to induce a syntax error.
  143.     // See http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx
  144.     var useSourceURL = (Function('//@cc_on !')(), true);
  145.   } catch(e){ }
  146.  
  147.   /** Used to identify object classifications that are array-like */
  148.   var arrayLikeClasses = {};
  149.   arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] =
  150.   arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false;
  151.   arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true;
  152.  
  153.   /** Used to identify object classifications that `_.clone` supports */
  154.   var cloneableClasses = {};
  155.   cloneableClasses[argsClass] = cloneableClasses[funcClass] = false;
  156.   cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] =
  157.   cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] =
  158.   cloneableClasses[stringClass] = true;
  159.  
  160.   /**
  161.    * Used to escape characters for inclusion in HTML.
  162.    * The `>` and `/` characters don't require escaping in HTML and have no
  163.    * special meaning unless they're part of a tag or an unquoted attribute value
  164.    * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact)
  165.    */
  166.   var htmlEscapes = {
  167.     '&': '&amp;',
  168.     '<': '&lt;',
  169.     '"': '&quot;',
  170.     "'": '&#x27;'
  171.   };
  172.  
  173.   /** Used to determine if values are of the language type Object */
  174.   var objectTypes = {
  175.     'boolean': false,
  176.     'function': true,
  177.     'object': true,
  178.     'number': false,
  179.     'string': false,
  180.     'undefined': false,
  181.     'unknown': true
  182.   };
  183.  
  184.   /** Used to escape characters for inclusion in compiled string literals */
  185.   var stringEscapes = {
  186.     '\\': '\\',
  187.     "'": "'",
  188.     '\n': 'n',
  189.     '\r': 'r',
  190.     '\t': 't',
  191.     '\u2028': 'u2028',
  192.     '\u2029': 'u2029'
  193.   };
  194.  
  195.   /*--------------------------------------------------------------------------*/
  196.  
  197.   /**
  198.    * The `lodash` function.
  199.    *
  200.    * @name _
  201.    * @constructor
  202.    * @param {Mixed} value The value to wrap in a `LoDash` instance.
  203.    * @returns {Object} Returns a `LoDash` instance.
  204.    */
  205.   function lodash(value) {
  206.     // allow invoking `lodash` without the `new` operator
  207.     return
  208.   }
  209.  
  210.   /**
  211.    * By default, the template delimiters used by Lo-Dash are similar to those in
  212.    * embedded Ruby (ERB). Change the following template settings to use alternative
  213.    * delimiters.
  214.    *
  215.    * @static
  216.    * @memberOf _
  217.    * @type Object
  218.    */
  219.   lodash.templateSettings = {
  220.  
  221.     /**
  222.      * Used to detect `data` property values to be HTML-escaped.
  223.      *
  224.      * @static
  225.      * @memberOf _.templateSettings
  226.      * @type RegExp
  227.      */
  228.     'escape': /<%-([\s\S]+?)%>/g,
  229.  
  230.     /**
  231.      * Used to detect code to be evaluated.
  232.      *
  233.      * @static
  234.      * @memberOf _.templateSettings
  235.      * @type RegExp
  236.      */
  237.     'evaluate': /<%([\s\S]+?)%>/g,
  238.  
  239.     /**
  240.      * Used to detect `data` property values to inject.
  241.      *
  242.      * @static
  243.      * @memberOf _.templateSettings
  244.      * @type RegExp
  245.      */
  246.     'interpolate': /<%=([\s\S]+?)%>/g,
  247.  
  248.     /**
  249.      * Used to reference the data object in the template text.
  250.      *
  251.      * @static
  252.      * @memberOf _.templateSettings
  253.      * @type String
  254.      */
  255.     'variable': 'obj'
  256.   };
  257.  
  258.   /*--------------------------------------------------------------------------*/
  259.  
  260.   /**
  261.    * The template used to create iterator functions.
  262.    *
  263.    * @private
  264.    * @param {Obect} data The data object used to populate the text.
  265.    * @returns {String} Returns the interpolated text.
  266.    */
  267.   var iteratorTemplate = function(obj) {
  268.    
  269.     var __p = '';
  270.    
  271.  
  272.  
  273.    
  274.      if (obj.useStrict) {
  275.     __p += '\'use strict\';\n';
  276.      } ;
  277.     __p += 'var index, value, iteratee = ' +
  278.     obj.firstArg  +
  279.     ', result';
  280.      if (obj.init) {
  281.     __p += ' = ' +
  282.     obj.init ;
  283.      } ;
  284.     __p += ';\n' +
  285.     obj.exit  +
  286.     ';\n' +
  287.     obj.top  +
  288.     ';\n';
  289.      if (obj.arrayBranch) {
  290.     __p += 'var length = iteratee.length; index = -1;  ';
  291.      if (obj.objectBranch) {
  292.     __p += '\nif (length === length >>> 0) {';
  293.      } ;
  294.    
  295.      if (obj.noCharByIndex) {
  296.     __p += '\n  if (toString.call(iteratee) == stringClass) {\n    iteratee = iteratee.split(\'\')\n  }  ';
  297.      } ;
  298.     __p +=
  299.     obj.arrayBranch.beforeLoop  +
  300.     ';\n  while (++index < length) {\n    value = iteratee[index];\n    ' +
  301.     obj.arrayBranch.inLoop  +
  302.     '\n  }  ';
  303.      if (obj.objectBranch) {
  304.     __p += '\n}';
  305.      } ;
  306.    
  307.      } ;
  308.    
  309.      if (obj.objectBranch) {
  310.    
  311.      if (obj.arrayBranch) {
  312.     __p += '\nelse {  ';
  313.       } else if (obj.noArgsEnum) {
  314.     __p += '\n  var length = iteratee.length; index = -1;\n  if (length && isArguments(iteratee)) {\n    while (++index < length) {\n      value = iteratee[index += \'\'];\n      ' +
  315.     obj.objectBranch.inLoop  +
  316.     '\n    }\n  } else {  ';
  317.      } ;
  318.    
  319.      if (!obj.hasDontEnumBug) {
  320.     __p += '\n  var skipProto = typeof iteratee == \'function\' && \n    propertyIsEnumerable.call(iteratee, \'prototype\');\n  ';
  321.      } ;
  322.    
  323.      if (obj.isKeysFast && obj.useHas) {
  324.     __p += '\n  var ownIndex = -1,\n      ownProps = nativeKeys(iteratee),\n      length = ownProps.length;\n\n  ' +
  325.     obj.objectBranch.beforeLoop  +
  326.     ';\n  while (++ownIndex < length) {\n    index = ownProps[ownIndex];\n    if (!(skipProto && index == \'prototype\')) {\n      value = iteratee[index];\n      ' +
  327.     obj.objectBranch.inLoop  +
  328.     '\n    }\n  }  ';
  329.      } else {
  330.     __p +=
  331.     obj.objectBranch.beforeLoop  +
  332.     ';\n  for (index in iteratee) {    ';
  333.      if (obj.hasDontEnumBug) {
  334.    
  335.        if (obj.useHas) {
  336.     __p += 'if (hasOwnProperty.call(iteratee, index)) {\n  ';
  337.      } ;
  338.     __p += '    value = iteratee[index];\n    ' +
  339.     obj.objectBranch.inLoop  +
  340.     ';\n    ';
  341.        if (obj.useHas) {
  342.     __p += '}';
  343.      } ;
  344.    
  345.      } else {
  346.     __p += '\n    if (!(skipProto && index == \'prototype\')';
  347.      if (obj.useHas) {
  348.     __p += ' &&\n        hasOwnProperty.call(iteratee, index)';
  349.      } ;
  350.     __p += ') {\n      value = iteratee[index];\n      ' +
  351.     obj.objectBranch.inLoop  +
  352.     '\n    }    ';
  353.      } ;
  354.     __p += '\n  }  ';
  355.      } ;
  356.    
  357.      if (obj.hasDontEnumBug) {
  358.     __p += '\n\n  var ctor = iteratee.constructor;\n    ';
  359.      for (var k = 0; k < 7; k++) {
  360.     __p += '\n  index = \'' +
  361.     obj.shadowed[k]  +
  362.     '\';\n  if (';
  363.           if (obj.shadowed[k] == 'constructor') {
  364.     __p += '!(ctor && ctor.prototype === iteratee) && ';
  365.           } ;
  366.     __p += 'hasOwnProperty.call(iteratee, index)) {\n    value = iteratee[index];\n    ' +
  367.     obj.objectBranch.inLoop  +
  368.     '\n  }    ';
  369.      } ;
  370.    
  371.      } ;
  372.    
  373.      if (obj.arrayBranch || obj.noArgsEnum) {
  374.     __p += '\n}';
  375.      } ;
  376.    
  377.      } ;
  378.     __p +=
  379.     obj.bottom  +
  380.     ';\nreturn result';
  381.    
  382.    
  383.     return __p
  384.   };
  385.  
  386.   /**
  387.    * Reusable iterator options shared by
  388.    * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`,
  389.    * `reject`, `some`, and `sortBy`.
  390.    */
  391.   var baseIteratorOptions = {
  392.     'args': 'collection, callback, thisArg',
  393.     'init': 'collection',
  394.     'top':
  395.       'if (!callback) {\n' +
  396.       '  callback = identity\n' +
  397.       '}\n' +
  398.       'else if (thisArg) callback = iteratorBind(callback, thisArg)',
  399.     'inLoop': 'if (callback(value, index, collection) === false) return result'
  400.   };
  401.  
  402.   /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */
  403.   var countByIteratorOptions = {
  404.     'init': '{}',
  405.     'top':
  406.       'var prop;\n' +
  407.       'if (typeof callback != \'function\') {\n' +
  408.       '  var valueProp = callback;\n' +
  409.       '  callback = function(value) { return value[valueProp] }\n' +
  410.       '}\n' +
  411.       'else if (thisArg) callback = iteratorBind(callback, thisArg)',
  412.     'inLoop':
  413.       'prop = callback(value, index, collection);\n' +
  414.       '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)'
  415.   };
  416.  
  417.   /** Reusable iterator options for `every` and `some` */
  418.   var everyIteratorOptions = {
  419.     'init': 'true',
  420.     'inLoop': 'if (!callback(value, index, collection)) return !result'
  421.   };
  422.  
  423.   /** Reusable iterator options for `defaults` and `extend` */
  424.   var extendIteratorOptions = {
  425.     'useHas': false,
  426.     'useStrict': false,
  427.     'args': 'object',
  428.     'init': 'object',
  429.     'top':
  430.       'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
  431.       '  if (iteratee = arguments[argsIndex]) {',
  432.     'inLoop': 'result[index] = value',
  433.     'bottom': '  }\n}'
  434.   };
  435.  
  436.   /** Reusable iterator options for `filter`, `reject`, and `where` */
  437.   var filterIteratorOptions = {
  438.     'init': '[]',
  439.     'inLoop': 'callback(value, index, collection) && result.push(value)'
  440.   };
  441.  
  442.   /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */
  443.   var forEachIteratorOptions = {
  444.     'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)'
  445.   };
  446.  
  447.   /** Reusable iterator options for `forIn` and `forOwn` */
  448.   var forOwnIteratorOptions = {
  449.     'inLoop': {
  450.       'object': baseIteratorOptions.inLoop
  451.     }
  452.   };
  453.  
  454.   /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */
  455.   var mapIteratorOptions = {
  456.     'init': '',
  457.     'exit': 'if (!collection) return []',
  458.     'beforeLoop': {
  459.       'array':  'result = Array(length)',
  460.       'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]')
  461.     },
  462.     'inLoop': {
  463.       'array':  'result[index] = callback(value, index, collection)',
  464.       'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))'
  465.     }
  466.   };
  467.  
  468.   /*--------------------------------------------------------------------------*/
  469.  
  470.   /**
  471.    * Creates a new function optimized for searching large arrays for a given `value`,
  472.    * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.
  473.    *
  474.    * @private
  475.    * @param {Array} array The array to search.
  476.    * @param {Mixed} value The value to search for.
  477.    * @param {Number} [fromIndex=0] The index to start searching from.
  478.    * @param {Number} [largeSize=30] The length at which an array is considered large.
  479.    * @returns {Boolean} Returns `true` if `value` is found, else `false`.
  480.    */
  481.   function cachedContains(array, fromIndex, largeSize) {
  482.     fromIndex || (fromIndex = 0);
  483.  
  484.     var length = array.length,
  485.         isLarge = (length - fromIndex) >= (largeSize || 30),
  486.         cache = isLarge ? {} : array;
  487.  
  488.     if (isLarge) {
  489.       // init value cache
  490.       var key,
  491.           index = fromIndex - 1;
  492.  
  493.       while (++index < length) {
  494.         // manually coerce `value` to string because `hasOwnProperty`, in some
  495.         // older versions of Firefox, coerces objects incorrectly
  496.         key = array[index] + '';
  497.         (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]);
  498.       }
  499.     }
  500.     return function(value) {
  501.       if (isLarge) {
  502.         var key = value + '';
  503.         return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1;
  504.       }
  505.       return indexOf(cache, value, fromIndex) > -1;
  506.     }
  507.   }
  508.  
  509.   /**
  510.    * Used by `sortBy` to compare transformed values of `collection`, sorting
  511.    * them in ascending order.
  512.    *
  513.    * @private
  514.    * @param {Object} a The object to compare to `b`.
  515.    * @param {Object} b The object to compare to `a`.
  516.    * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`.
  517.    */
  518.   function compareAscending(a, b) {
  519.     a = a.criteria;
  520.     b = b.criteria;
  521.  
  522.     if (a === undefined) {
  523.       return 1;
  524.     }
  525.     if (b === undefined) {
  526.       return -1;
  527.     }
  528.     return a < b ? -1 : a > b ? 1 : 0;
  529.   }
  530.  
  531.   /**
  532.    * Used by `template` to replace tokens with their corresponding code snippets.
  533.    *
  534.    * @private
  535.    * @param {String} match The matched token.
  536.    * @param {String} index The `tokenized` index of the code snippet.
  537.    * @returns {String} Returns the code snippet.
  538.    */
  539.   function detokenize(match, index) {
  540.     return tokenized[index];
  541.   }
  542.  
  543.   /**
  544.    * Used by `template` to escape characters for inclusion in compiled
  545.    * string literals.
  546.    *
  547.    * @private
  548.    * @param {String} match The matched character to escape.
  549.    * @returns {String} Returns the escaped character.
  550.    */
  551.   function escapeStringChar(match) {
  552.     return '\\' + stringEscapes[match];
  553.   }
  554.  
  555.   /**
  556.    * Used by `escape` to escape characters for inclusion in HTML.
  557.    *
  558.    * @private
  559.    * @param {String} match The matched character to escape.
  560.    * @returns {String} Returns the escaped character.
  561.    */
  562.   function escapeHtmlChar(match) {
  563.     return htmlEscapes[match];
  564.   }
  565.  
  566.   /**
  567.    * Creates a new function that, when called, invokes `func` with the `this`
  568.    * binding of `thisArg` and the arguments (value, index, object).
  569.    *
  570.    * @private
  571.    * @param {Function} func The function to bind.
  572.    * @param {Mixed} [thisArg] The `this` binding of `func`.
  573.    * @returns {Function} Returns the new bound function.
  574.    */
  575.   function iteratorBind(func, thisArg) {
  576.     return function(value, index, object) {
  577.       return func.call(thisArg, value, index, object);
  578.     };
  579.   }
  580.  
  581.   /**
  582.    * A no-operation function.
  583.    *
  584.    * @private
  585.    */
  586.   function noop() {
  587.     // no operation performed
  588.   }
  589.  
  590.   /**
  591.    * Used by `template` to replace "escape" template delimiters with tokens.
  592.    *
  593.    * @private
  594.    * @param {String} match The matched template delimiter.
  595.    * @param {String} value The delimiter value.
  596.    * @returns {String} Returns a token.
  597.    */
  598.   function tokenizeEscape(match, value) {
  599.     if (reComplexDelimiter.test(value)) {
  600.       return '<e%-' + value + '%>';
  601.     }
  602.     var index = tokenized.length;
  603.     tokenized[index] = "' +\n__e(" + value + ") +\n'";
  604.     return token + index;
  605.   }
  606.  
  607.   /**
  608.    * Used by `template` to replace "evaluate" template delimiters, or complex
  609.    * "escape" and "interpolate" delimiters, with tokens.
  610.    *
  611.    * @private
  612.    * @param {String} match The matched template delimiter.
  613.    * @param {String} escapeValue The complex "escape" delimiter value.
  614.    * @param {String} interpolateValue The complex "interpolate" delimiter value.
  615.    * @param {String} [evaluateValue] The "evaluate" delimiter value.
  616.    * @returns {String} Returns a token.
  617.    */
  618.   function tokenizeEvaluate(match, escapeValue, interpolateValue, evaluateValue) {
  619.     var index = tokenized.length;
  620.     if (escapeValue) {
  621.       tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'";
  622.     } else if (evaluateValue) {
  623.       tokenized[index] = "';\n" + evaluateValue + ";\n__p += '";
  624.     } else if (interpolateValue) {
  625.       tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
  626.     }
  627.     return token + index;
  628.   }
  629.  
  630.   /**
  631.    * Used by `template` to replace "interpolate" template delimiters with tokens.
  632.    *
  633.    * @private
  634.    * @param {String} match The matched template delimiter.
  635.    * @param {String} value The delimiter value.
  636.    * @returns {String} Returns a token.
  637.    */
  638.   function tokenizeInterpolate(match, value) {
  639.     if (reComplexDelimiter.test(value)) {
  640.       return '<e%=' + value + '%>';
  641.     }
  642.     var index = tokenized.length;
  643.     tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'";
  644.     return token + index;
  645.   }
  646.  
  647.   /*--------------------------------------------------------------------------*/
  648.  
  649.   /**
  650.    * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'`
  651.    * characters.
  652.    *
  653.    * @static
  654.    * @memberOf _
  655.    * @category Utilities
  656.    * @param {String} string The string to escape.
  657.    * @returns {String} Returns the escaped string.
  658.    * @example
  659.    *
  660.    * _.escape('Moe, Larry & Curly');
  661.    * // => "Moe, Larry &amp; Curly"
  662.    */
  663.   function escape(string) {
  664.     return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar);
  665.   }
  666.  
  667.   /**
  668.    * A micro-templating method that handles arbitrary delimiters, preserves
  669.    * whitespace, and correctly escapes quotes within interpolated code.
  670.    *
  671.    * Note: For Chrome extensions use the `lodash csp` build and see
  672.    * http://code.google.com/chrome/extensions/trunk/sandboxingEval.html
  673.    *
  674.    * @static
  675.    * @memberOf _
  676.    * @category Utilities
  677.    * @param {String} text The template text.
  678.    * @param {Obect} data The data object used to populate the text.
  679.    * @param {Object} options The options object.
  680.    * @returns {Function|String} Returns a compiled function when no `data` object
  681.    *  is given, else it returns the interpolated text.
  682.    * @example
  683.    *
  684.    * // using compiled template
  685.    * var compiled = _.template('hello: <%= name %>');
  686.    * compiled({ 'name': 'moe' });
  687.    * // => 'hello: moe'
  688.    *
  689.    * var list = '<% _.forEach(people, function(name) { %> <li><%= name %></li> <% }); %>';
  690.    * _.template(list, { 'people': ['moe', 'larry', 'curly'] });
  691.    * // => '<li>moe</li><li>larry</li><li>curly</li>'
  692.    *
  693.    * var template = _.template('<b><%- value %></b>');
  694.    * template({ 'value': '<script>' });
  695.    * // => '<b>&lt;script></b>'
  696.    *
  697.    * // using `print`
  698.    * var compiled = _.template('<% print("Hello " + epithet); %>');
  699.    * compiled({ 'epithet': 'stooge' });
  700.    * // => 'Hello stooge.'
  701.    *
  702.    * // using custom template settings
  703.    * _.templateSettings = {
  704.    *   'interpolate': /\{\{(.+?)\}\}/g
  705.    * };
  706.    *
  707.    * var template = _.template('Hello {{ name }}!');
  708.    * template({ 'name': 'Mustache' });
  709.    * // => 'Hello Mustache!'
  710.    *
  711.    * // using the `variable` option
  712.    * _.template('<%= data.hasWith %>', { 'hasWith': 'no' }, { 'variable': 'data' });
  713.    * // => 'no'
  714.    *
  715.    * // using the `source` property
  716.    * <script>
  717.    *   JST.project = <%= _.template(jstText).source %>;
  718.    * </script>
  719.    */
  720.   function template(text, data, options) {
  721.     // based on John Resig's `tmpl` implementation
  722.     // http://ejohn.org/blog/javascript-micro-templating/
  723.     // and Laura Doktorova's doT.js
  724.     // https://github.com/olado/doT
  725.     options || (options = {});
  726.  
  727.     var isEvaluating,
  728.         result,
  729.         escapeDelimiter = options.escape,
  730.         evaluateDelimiter = options.evaluate,
  731.         interpolateDelimiter = options.interpolate,
  732.         settings = lodash.templateSettings,
  733.         variable = options.variable;
  734.  
  735.     // use default settings if no options object is provided
  736.     if (escapeDelimiter == null) {
  737.       escapeDelimiter = settings.escape;
  738.     }
  739.     if (evaluateDelimiter == null) {
  740.       // use `false` as the fallback value, instead of leaving it `undefined`,
  741.       // so the initial assignment of `reEvaluateDelimiter` will still occur
  742.       evaluateDelimiter = settings.evaluate || false;
  743.     }
  744.     if (interpolateDelimiter == null) {
  745.       interpolateDelimiter = settings.interpolate;
  746.     }
  747.  
  748.     // tokenize delimiters to avoid escaping them
  749.     if (escapeDelimiter) {
  750.       text = text.replace(escapeDelimiter, tokenizeEscape);
  751.     }
  752.     if (interpolateDelimiter) {
  753.       text = text.replace(interpolateDelimiter, tokenizeInterpolate);
  754.     }
  755.     if (evaluateDelimiter != lastEvaluateDelimiter) {
  756.       // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate`
  757.       // and internal `<e%- %>`, `<e%= %>` delimiters
  758.       lastEvaluateDelimiter = evaluateDelimiter;
  759.       reEvaluateDelimiter = RegExp(
  760.         '<e%-([\\s\\S]+?)%>|<e%=([\\s\\S]+?)%>' +
  761.         (evaluateDelimiter ? '|' + evaluateDelimiter.source : '')
  762.       , 'g');
  763.     }
  764.     isEvaluating = tokenized.length;
  765.     text = text.replace(reEvaluateDelimiter, tokenizeEvaluate);
  766.     isEvaluating = isEvaluating != tokenized.length;
  767.  
  768.     // escape characters that cannot be included in string literals and
  769.     // detokenize delimiter code snippets
  770.     text = "__p += '" + text
  771.       .replace(reUnescapedString, escapeStringChar)
  772.       .replace(reToken, detokenize) + "';\n";
  773.  
  774.     // clear stored code snippets
  775.     tokenized.length = 0;
  776.  
  777.     // if `options.variable` is not specified and the template contains "evaluate"
  778.     // delimiters, wrap a with-statement around the generated code to add the
  779.     // data object to the top of the scope chain
  780.     if (!variable) {
  781.       variable = settings.variable || lastVariable || 'obj';
  782.  
  783.       if (isEvaluating) {
  784.         text = 'with (' + variable + ') {\n' + text + '\n}\n';
  785.       }
  786.       else {
  787.         if (variable != lastVariable) {
  788.           // generate `reDoubleVariable` to match references like `obj.obj` inside
  789.           // transformed "escape" and "interpolate" delimiters
  790.           lastVariable = variable;
  791.           reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g');
  792.         }
  793.         // avoid a with-statement by prepending data object references to property names
  794.         text = text
  795.           .replace(reInsertVariable, '$&' + variable + '.')
  796.           .replace(reDoubleVariable, '$1__d');
  797.       }
  798.     }
  799.  
  800.     // cleanup code by stripping empty strings
  801.     text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text)
  802.       .replace(reEmptyStringMiddle, '$1')
  803.       .replace(reEmptyStringTrailing, '$1;');
  804.  
  805.     // frame code as the function body
  806.     text = 'function(' + variable + ') {\n' +
  807.       variable + ' || (' + variable + ' = {});\n' +
  808.       'var __t, __p = \'\', __e = _.escape' +
  809.       (isEvaluating
  810.         ? ', __j = Array.prototype.join;\n' +
  811.           'function print() { __p += __j.call(arguments, \'\') }\n'
  812.         : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n'
  813.       ) +
  814.       text +
  815.       'return __p\n}';
  816.  
  817.     // add a sourceURL for easier debugging
  818.     // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
  819.     if (useSourceURL) {
  820.       text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']';
  821.     }
  822.  
  823.     try {
  824.       result = Function('_', 'return ' + text)(lodash);
  825.     } catch(e) {
  826.       // defer syntax errors until the compiled template is executed to allow
  827.       // examining the `source` property beforehand and for consistency,
  828.       // because other template related errors occur at execution
  829.       result = function() { throw e; };
  830.     }
  831.  
  832.     if (data) {
  833.       return result(data);
  834.     }
  835.     // provide the compiled function's source via its `toString` method, in
  836.     // supported environments, or the `source` property as a convenience for
  837.     // build time precompilation
  838.     result.source = text;
  839.     return result;
  840.   }
  841.  
  842.   /*--------------------------------------------------------------------------*/
  843.  
  844.   /**
  845.    * Enables method chaining on the wrapper object.
  846.    *
  847.    * @name chain
  848.    * @deprecated
  849.    * @memberOf _
  850.    * @category Chaining
  851.    * @returns {Mixed} Returns the wrapper object.
  852.    * @example
  853.    *
  854.    * _([1, 2, 3]).value();
  855.    * // => [1, 2, 3]
  856.    */
  857.   function wrapperChain() {
  858.     this._chain = true;
  859.     return this;
  860.   }
  861.  
  862.   /**
  863.    * Extracts the wrapped value.
  864.    *
  865.    * @name value
  866.    * @memberOf _
  867.    * @category Chaining
  868.    * @returns {Mixed} Returns the wrapped value.
  869.    * @example
  870.    *
  871.    * _([1, 2, 3]).value();
  872.    * // => [1, 2, 3]
  873.    */
  874.   function wrapperValue() {
  875.     return this._wrapped;
  876.   }
  877.  
  878.   /*--------------------------------------------------------------------------*/
  879.  
  880.   /**
  881.    * The semantic version number.
  882.    *
  883.    * @static
  884.    * @memberOf _
  885.    * @type String
  886.    */
  887.   lodash.VERSION = '0.5.0-rc.1';
  888.   lodash.escape = escape;
  889.   lodash.template = template;
  890.  
  891.   /*--------------------------------------------------------------------------*/
  892.  
  893.   // expose Lo-Dash
  894.   // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
  895.   if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
  896.     // Expose Lo-Dash to the global object even when an AMD loader is present in
  897.     // case Lo-Dash was injected by a third-party script and not intended to be
  898.     // loaded as a module. The global assignment can be reverted in the Lo-Dash
  899.     // module via its `noConflict()` method.
  900.     window._ = lodash;
  901.  
  902.     // define as an anonymous module so, through path mapping, it can be
  903.     // referenced as the "underscore" module
  904.     define(function() {
  905.       return lodash;
  906.     });
  907.   }
  908.   // check for `exports` after `define` in case a build optimizer adds an `exports` object
  909.   else if (freeExports) {
  910.     // in Node.js or RingoJS v0.8.0+
  911.     if (typeof module == 'object' && module && module.exports == freeExports) {
  912.       (module.exports = lodash)._ = lodash;
  913.     }
  914.     // in Narwhal or RingoJS v0.7.0-
  915.     else {
  916.       freeExports._ = lodash;
  917.     }
  918.   }
  919.   else {
  920.     // in a browser or Rhino
  921.     window._ = lodash;
  922.   }
  923. }(this));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement