blob: 4f5449ecc7d246e65d137f853176aef5a0d53fec [file] [log] [blame]
Tim van der Lippe38208902021-05-11 16:37:59 +01001/**
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`. */
11var DEFAULT_TRUNC_LENGTH = 30,
12 DEFAULT_TRUNC_OMISSION = '...';
13
14/** Used as references for various `Number` constants. */
15var INFINITY = 1 / 0,
16 MAX_INTEGER = 1.7976931348623157e+308,
17 NAN = 0 / 0;
18
19/** `Object#toString` result references. */
20var regexpTag = '[object RegExp]',
21 symbolTag = '[object Symbol]';
22
23/** Used to match leading and trailing whitespace. */
24var reTrim = /^\s+|\s+$/g;
25
26/** Used to match `RegExp` flags from their coerced string values. */
27var reFlags = /\w*$/;
28
29/** Used to detect bad signed hexadecimal string values. */
30var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
31
32/** Used to detect binary string values. */
33var reIsBinary = /^0b[01]+$/i;
34
35/** Used to detect octal string values. */
36var reIsOctal = /^0o[0-7]+$/i;
37
38/** Used to compose unicode character classes. */
39var 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. */
45var 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. */
55var 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). */
62var 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/). */
65var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
66
67/** Built-in method references without a dependency on `root`. */
68var freeParseInt = parseInt;
69
70/** Detect free variable `global` from Node.js. */
71var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
72
73/** Detect free variable `self`. */
74var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
75
76/** Used as a reference to the global object. */
77var root = freeGlobal || freeSelf || Function('return this')();
78
79/** Detect free variable `exports`. */
80var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
81
82/** Detect free variable `module`. */
83var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
84
85/** Detect the popular CommonJS extension `module.exports`. */
86var moduleExports = freeModule && freeModule.exports === freeExports;
87
88/** Detect free variable `process` from Node.js. */
89var freeProcess = moduleExports && freeGlobal.process;
90
91/** Used to access faster Node.js helpers. */
92var nodeUtil = (function() {
93 try {
94 return freeProcess && freeProcess.binding('util');
95 } catch (e) {}
96}());
97
98/* Node.js helper references. */
99var 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 */
108var 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 */
117function 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 */
128function 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 */
141function 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 */
154function 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 */
165function 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 */
178function 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 */
191function 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 */
206function unicodeToArray(string) {
207 return string.match(reUnicode) || [];
208}
209
210/** Used for built-in method references. */
211var 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 */
218var objectToString = objectProto.toString;
219
220/** Built-in value references. */
221var Symbol = root.Symbol;
222
223/** Used to convert symbols to primitives and strings. */
224var 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 */
234function 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 */
247function 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 */
276function 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 */
297function 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 */
328function 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 */
357function 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 */
378var 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 */
397function 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 */
425function 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 */
463function 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 */
493function 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 */
535function 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 */
576function 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
632module.exports = truncate;