index.js
  1  /**
  2   * lodash (Custom Build) <https://lodash.com/>
  3   * Build: `lodash modularize exports="npm" -o ./`
  4   * Copyright jQuery Foundation and other contributors <https://jquery.org/>
  5   * Released under MIT license <https://lodash.com/license>
  6   * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
  7   * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
  8   */
  9  
 10  /** Used as default options for `_.truncate`. */
 11  var DEFAULT_TRUNC_LENGTH = 30,
 12      DEFAULT_TRUNC_OMISSION = '...';
 13  
 14  /** Used as references for various `Number` constants. */
 15  var INFINITY = 1 / 0,
 16      MAX_INTEGER = 1.7976931348623157e+308,
 17      NAN = 0 / 0;
 18  
 19  /** `Object#toString` result references. */
 20  var regexpTag = '[object RegExp]',
 21      symbolTag = '[object Symbol]';
 22  
 23  /** Used to match leading and trailing whitespace. */
 24  var reTrim = /^\s+|\s+$/g;
 25  
 26  /** Used to match `RegExp` flags from their coerced string values. */
 27  var reFlags = /\w*$/;
 28  
 29  /** Used to detect bad signed hexadecimal string values. */
 30  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
 31  
 32  /** Used to detect binary string values. */
 33  var reIsBinary = /^0b[01]+$/i;
 34  
 35  /** Used to detect octal string values. */
 36  var reIsOctal = /^0o[0-7]+$/i;
 37  
 38  /** Used to compose unicode character classes. */
 39  var rsAstralRange = '\\ud800-\\udfff',
 40      rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
 41      rsComboSymbolsRange = '\\u20d0-\\u20f0',
 42      rsVarRange = '\\ufe0e\\ufe0f';
 43  
 44  /** Used to compose unicode capture groups. */
 45  var rsAstral = '[' + rsAstralRange + ']',
 46      rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
 47      rsFitz = '\\ud83c[\\udffb-\\udfff]',
 48      rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
 49      rsNonAstral = '[^' + rsAstralRange + ']',
 50      rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
 51      rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
 52      rsZWJ = '\\u200d';
 53  
 54  /** Used to compose unicode regexes. */
 55  var reOptMod = rsModifier + '?',
 56      rsOptVar = '[' + rsVarRange + ']?',
 57      rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
 58      rsSeq = rsOptVar + reOptMod + rsOptJoin,
 59      rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
 60  
 61  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
 62  var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
 63  
 64  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
 65  var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
 66  
 67  /** Built-in method references without a dependency on `root`. */
 68  var freeParseInt = parseInt;
 69  
 70  /** Detect free variable `global` from Node.js. */
 71  var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
 72  
 73  /** Detect free variable `self`. */
 74  var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
 75  
 76  /** Used as a reference to the global object. */
 77  var root = freeGlobal || freeSelf || Function('return this')();
 78  
 79  /** Detect free variable `exports`. */
 80  var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
 81  
 82  /** Detect free variable `module`. */
 83  var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
 84  
 85  /** Detect the popular CommonJS extension `module.exports`. */
 86  var moduleExports = freeModule && freeModule.exports === freeExports;
 87  
 88  /** Detect free variable `process` from Node.js. */
 89  var freeProcess = moduleExports && freeGlobal.process;
 90  
 91  /** Used to access faster Node.js helpers. */
 92  var nodeUtil = (function() {
 93    try {
 94      return freeProcess && freeProcess.binding('util');
 95    } catch (e) {}
 96  }());
 97  
 98  /* Node.js helper references. */
 99  var nodeIsRegExp = nodeUtil && nodeUtil.isRegExp;
100  
101  /**
102   * Gets the size of an ASCII `string`.
103   *
104   * @private
105   * @param {string} string The string inspect.
106   * @returns {number} Returns the string size.
107   */
108  var asciiSize = baseProperty('length');
109  
110  /**
111   * Converts an ASCII `string` to an array.
112   *
113   * @private
114   * @param {string} string The string to convert.
115   * @returns {Array} Returns the converted array.
116   */
117  function asciiToArray(string) {
118    return string.split('');
119  }
120  
121  /**
122   * The base implementation of `_.property` without support for deep paths.
123   *
124   * @private
125   * @param {string} key The key of the property to get.
126   * @returns {Function} Returns the new accessor function.
127   */
128  function baseProperty(key) {
129    return function(object) {
130      return object == null ? undefined : object[key];
131    };
132  }
133  
134  /**
135   * The base implementation of `_.unary` without support for storing metadata.
136   *
137   * @private
138   * @param {Function} func The function to cap arguments for.
139   * @returns {Function} Returns the new capped function.
140   */
141  function baseUnary(func) {
142    return function(value) {
143      return func(value);
144    };
145  }
146  
147  /**
148   * Checks if `string` contains Unicode symbols.
149   *
150   * @private
151   * @param {string} string The string to inspect.
152   * @returns {boolean} Returns `true` if a symbol is found, else `false`.
153   */
154  function hasUnicode(string) {
155    return reHasUnicode.test(string);
156  }
157  
158  /**
159   * Gets the number of symbols in `string`.
160   *
161   * @private
162   * @param {string} string The string to inspect.
163   * @returns {number} Returns the string size.
164   */
165  function stringSize(string) {
166    return hasUnicode(string)
167      ? unicodeSize(string)
168      : asciiSize(string);
169  }
170  
171  /**
172   * Converts `string` to an array.
173   *
174   * @private
175   * @param {string} string The string to convert.
176   * @returns {Array} Returns the converted array.
177   */
178  function stringToArray(string) {
179    return hasUnicode(string)
180      ? unicodeToArray(string)
181      : asciiToArray(string);
182  }
183  
184  /**
185   * Gets the size of a Unicode `string`.
186   *
187   * @private
188   * @param {string} string The string inspect.
189   * @returns {number} Returns the string size.
190   */
191  function unicodeSize(string) {
192    var result = reUnicode.lastIndex = 0;
193    while (reUnicode.test(string)) {
194      result++;
195    }
196    return result;
197  }
198  
199  /**
200   * Converts a Unicode `string` to an array.
201   *
202   * @private
203   * @param {string} string The string to convert.
204   * @returns {Array} Returns the converted array.
205   */
206  function unicodeToArray(string) {
207    return string.match(reUnicode) || [];
208  }
209  
210  /** Used for built-in method references. */
211  var objectProto = Object.prototype;
212  
213  /**
214   * Used to resolve the
215   * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
216   * of values.
217   */
218  var objectToString = objectProto.toString;
219  
220  /** Built-in value references. */
221  var Symbol = root.Symbol;
222  
223  /** Used to convert symbols to primitives and strings. */
224  var symbolProto = Symbol ? Symbol.prototype : undefined,
225      symbolToString = symbolProto ? symbolProto.toString : undefined;
226  
227  /**
228   * The base implementation of `_.isRegExp` without Node.js optimizations.
229   *
230   * @private
231   * @param {*} value The value to check.
232   * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
233   */
234  function baseIsRegExp(value) {
235    return isObject(value) && objectToString.call(value) == regexpTag;
236  }
237  
238  /**
239   * The base implementation of `_.slice` without an iteratee call guard.
240   *
241   * @private
242   * @param {Array} array The array to slice.
243   * @param {number} [start=0] The start position.
244   * @param {number} [end=array.length] The end position.
245   * @returns {Array} Returns the slice of `array`.
246   */
247  function baseSlice(array, start, end) {
248    var index = -1,
249        length = array.length;
250  
251    if (start < 0) {
252      start = -start > length ? 0 : (length + start);
253    }
254    end = end > length ? length : end;
255    if (end < 0) {
256      end += length;
257    }
258    length = start > end ? 0 : ((end - start) >>> 0);
259    start >>>= 0;
260  
261    var result = Array(length);
262    while (++index < length) {
263      result[index] = array[index + start];
264    }
265    return result;
266  }
267  
268  /**
269   * The base implementation of `_.toString` which doesn't convert nullish
270   * values to empty strings.
271   *
272   * @private
273   * @param {*} value The value to process.
274   * @returns {string} Returns the string.
275   */
276  function baseToString(value) {
277    // Exit early for strings to avoid a performance hit in some environments.
278    if (typeof value == 'string') {
279      return value;
280    }
281    if (isSymbol(value)) {
282      return symbolToString ? symbolToString.call(value) : '';
283    }
284    var result = (value + '');
285    return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
286  }
287  
288  /**
289   * Casts `array` to a slice if it's needed.
290   *
291   * @private
292   * @param {Array} array The array to inspect.
293   * @param {number} start The start position.
294   * @param {number} [end=array.length] The end position.
295   * @returns {Array} Returns the cast slice.
296   */
297  function castSlice(array, start, end) {
298    var length = array.length;
299    end = end === undefined ? length : end;
300    return (!start && end >= length) ? array : baseSlice(array, start, end);
301  }
302  
303  /**
304   * Checks if `value` is the
305   * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
306   * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
307   *
308   * @static
309   * @memberOf _
310   * @since 0.1.0
311   * @category Lang
312   * @param {*} value The value to check.
313   * @returns {boolean} Returns `true` if `value` is an object, else `false`.
314   * @example
315   *
316   * _.isObject({});
317   * // => true
318   *
319   * _.isObject([1, 2, 3]);
320   * // => true
321   *
322   * _.isObject(_.noop);
323   * // => true
324   *
325   * _.isObject(null);
326   * // => false
327   */
328  function isObject(value) {
329    var type = typeof value;
330    return !!value && (type == 'object' || type == 'function');
331  }
332  
333  /**
334   * Checks if `value` is object-like. A value is object-like if it's not `null`
335   * and has a `typeof` result of "object".
336   *
337   * @static
338   * @memberOf _
339   * @since 4.0.0
340   * @category Lang
341   * @param {*} value The value to check.
342   * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
343   * @example
344   *
345   * _.isObjectLike({});
346   * // => true
347   *
348   * _.isObjectLike([1, 2, 3]);
349   * // => true
350   *
351   * _.isObjectLike(_.noop);
352   * // => false
353   *
354   * _.isObjectLike(null);
355   * // => false
356   */
357  function isObjectLike(value) {
358    return !!value && typeof value == 'object';
359  }
360  
361  /**
362   * Checks if `value` is classified as a `RegExp` object.
363   *
364   * @static
365   * @memberOf _
366   * @since 0.1.0
367   * @category Lang
368   * @param {*} value The value to check.
369   * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
370   * @example
371   *
372   * _.isRegExp(/abc/);
373   * // => true
374   *
375   * _.isRegExp('/abc/');
376   * // => false
377   */
378  var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;
379  
380  /**
381   * Checks if `value` is classified as a `Symbol` primitive or object.
382   *
383   * @static
384   * @memberOf _
385   * @since 4.0.0
386   * @category Lang
387   * @param {*} value The value to check.
388   * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
389   * @example
390   *
391   * _.isSymbol(Symbol.iterator);
392   * // => true
393   *
394   * _.isSymbol('abc');
395   * // => false
396   */
397  function isSymbol(value) {
398    return typeof value == 'symbol' ||
399      (isObjectLike(value) && objectToString.call(value) == symbolTag);
400  }
401  
402  /**
403   * Converts `value` to a finite number.
404   *
405   * @static
406   * @memberOf _
407   * @since 4.12.0
408   * @category Lang
409   * @param {*} value The value to convert.
410   * @returns {number} Returns the converted number.
411   * @example
412   *
413   * _.toFinite(3.2);
414   * // => 3.2
415   *
416   * _.toFinite(Number.MIN_VALUE);
417   * // => 5e-324
418   *
419   * _.toFinite(Infinity);
420   * // => 1.7976931348623157e+308
421   *
422   * _.toFinite('3.2');
423   * // => 3.2
424   */
425  function toFinite(value) {
426    if (!value) {
427      return value === 0 ? value : 0;
428    }
429    value = toNumber(value);
430    if (value === INFINITY || value === -INFINITY) {
431      var sign = (value < 0 ? -1 : 1);
432      return sign * MAX_INTEGER;
433    }
434    return value === value ? value : 0;
435  }
436  
437  /**
438   * Converts `value` to an integer.
439   *
440   * **Note:** This method is loosely based on
441   * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
442   *
443   * @static
444   * @memberOf _
445   * @since 4.0.0
446   * @category Lang
447   * @param {*} value The value to convert.
448   * @returns {number} Returns the converted integer.
449   * @example
450   *
451   * _.toInteger(3.2);
452   * // => 3
453   *
454   * _.toInteger(Number.MIN_VALUE);
455   * // => 0
456   *
457   * _.toInteger(Infinity);
458   * // => 1.7976931348623157e+308
459   *
460   * _.toInteger('3.2');
461   * // => 3
462   */
463  function toInteger(value) {
464    var result = toFinite(value),
465        remainder = result % 1;
466  
467    return result === result ? (remainder ? result - remainder : result) : 0;
468  }
469  
470  /**
471   * Converts `value` to a number.
472   *
473   * @static
474   * @memberOf _
475   * @since 4.0.0
476   * @category Lang
477   * @param {*} value The value to process.
478   * @returns {number} Returns the number.
479   * @example
480   *
481   * _.toNumber(3.2);
482   * // => 3.2
483   *
484   * _.toNumber(Number.MIN_VALUE);
485   * // => 5e-324
486   *
487   * _.toNumber(Infinity);
488   * // => Infinity
489   *
490   * _.toNumber('3.2');
491   * // => 3.2
492   */
493  function toNumber(value) {
494    if (typeof value == 'number') {
495      return value;
496    }
497    if (isSymbol(value)) {
498      return NAN;
499    }
500    if (isObject(value)) {
501      var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
502      value = isObject(other) ? (other + '') : other;
503    }
504    if (typeof value != 'string') {
505      return value === 0 ? value : +value;
506    }
507    value = value.replace(reTrim, '');
508    var isBinary = reIsBinary.test(value);
509    return (isBinary || reIsOctal.test(value))
510      ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
511      : (reIsBadHex.test(value) ? NAN : +value);
512  }
513  
514  /**
515   * Converts `value` to a string. An empty string is returned for `null`
516   * and `undefined` values. The sign of `-0` is preserved.
517   *
518   * @static
519   * @memberOf _
520   * @since 4.0.0
521   * @category Lang
522   * @param {*} value The value to process.
523   * @returns {string} Returns the string.
524   * @example
525   *
526   * _.toString(null);
527   * // => ''
528   *
529   * _.toString(-0);
530   * // => '-0'
531   *
532   * _.toString([1, 2, 3]);
533   * // => '1,2,3'
534   */
535  function toString(value) {
536    return value == null ? '' : baseToString(value);
537  }
538  
539  /**
540   * Truncates `string` if it's longer than the given maximum string length.
541   * The last characters of the truncated string are replaced with the omission
542   * string which defaults to "...".
543   *
544   * @static
545   * @memberOf _
546   * @since 4.0.0
547   * @category String
548   * @param {string} [string=''] The string to truncate.
549   * @param {Object} [options={}] The options object.
550   * @param {number} [options.length=30] The maximum string length.
551   * @param {string} [options.omission='...'] The string to indicate text is omitted.
552   * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
553   * @returns {string} Returns the truncated string.
554   * @example
555   *
556   * _.truncate('hi-diddly-ho there, neighborino');
557   * // => 'hi-diddly-ho there, neighbo...'
558   *
559   * _.truncate('hi-diddly-ho there, neighborino', {
560   *   'length': 24,
561   *   'separator': ' '
562   * });
563   * // => 'hi-diddly-ho there,...'
564   *
565   * _.truncate('hi-diddly-ho there, neighborino', {
566   *   'length': 24,
567   *   'separator': /,? +/
568   * });
569   * // => 'hi-diddly-ho there...'
570   *
571   * _.truncate('hi-diddly-ho there, neighborino', {
572   *   'omission': ' [...]'
573   * });
574   * // => 'hi-diddly-ho there, neig [...]'
575   */
576  function truncate(string, options) {
577    var length = DEFAULT_TRUNC_LENGTH,
578        omission = DEFAULT_TRUNC_OMISSION;
579  
580    if (isObject(options)) {
581      var separator = 'separator' in options ? options.separator : separator;
582      length = 'length' in options ? toInteger(options.length) : length;
583      omission = 'omission' in options ? baseToString(options.omission) : omission;
584    }
585    string = toString(string);
586  
587    var strLength = string.length;
588    if (hasUnicode(string)) {
589      var strSymbols = stringToArray(string);
590      strLength = strSymbols.length;
591    }
592    if (length >= strLength) {
593      return string;
594    }
595    var end = length - stringSize(omission);
596    if (end < 1) {
597      return omission;
598    }
599    var result = strSymbols
600      ? castSlice(strSymbols, 0, end).join('')
601      : string.slice(0, end);
602  
603    if (separator === undefined) {
604      return result + omission;
605    }
606    if (strSymbols) {
607      end += (result.length - end);
608    }
609    if (isRegExp(separator)) {
610      if (string.slice(end).search(separator)) {
611        var match,
612            substring = result;
613  
614        if (!separator.global) {
615          separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');
616        }
617        separator.lastIndex = 0;
618        while ((match = separator.exec(substring))) {
619          var newEnd = match.index;
620        }
621        result = result.slice(0, newEnd === undefined ? end : newEnd);
622      }
623    } else if (string.indexOf(baseToString(separator), end) != end) {
624      var index = result.lastIndexOf(separator);
625      if (index > -1) {
626        result = result.slice(0, index);
627      }
628    }
629    return result + omission;
630  }
631  
632  module.exports = truncate;