UNPKG

3.77 kBJavaScriptView Raw
1import isObject from './isObject';
2import isRegExp from './isRegExp';
3import stringSize from './_stringSize';
4import stringToArray from './_stringToArray';
5import toInteger from './toInteger';
6import toString from './toString';
7
8/** Used as default options for `_.truncate`. */
9var DEFAULT_TRUNC_LENGTH = 30,
10 DEFAULT_TRUNC_OMISSION = '...';
11
12/** Used to match `RegExp` flags from their coerced string values. */
13var reFlags = /\w*$/;
14
15/** Used to compose unicode character classes. */
16var rsAstralRange = '\\ud800-\\udfff',
17 rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
18 rsComboSymbolsRange = '\\u20d0-\\u20f0',
19 rsVarRange = '\\ufe0e\\ufe0f';
20
21/** Used to compose unicode capture groups. */
22var rsZWJ = '\\u200d';
23
24/** 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/). */
25var reHasComplexSymbol = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
26
27/**
28 * Truncates `string` if it's longer than the given maximum string length.
29 * The last characters of the truncated string are replaced with the omission
30 * string which defaults to "...".
31 *
32 * @static
33 * @memberOf _
34 * @since 4.0.0
35 * @category String
36 * @param {string} [string=''] The string to truncate.
37 * @param {Object} [options={}] The options object.
38 * @param {number} [options.length=30] The maximum string length.
39 * @param {string} [options.omission='...'] The string to indicate text is omitted.
40 * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
41 * @returns {string} Returns the truncated string.
42 * @example
43 *
44 * _.truncate('hi-diddly-ho there, neighborino');
45 * // => 'hi-diddly-ho there, neighbo...'
46 *
47 * _.truncate('hi-diddly-ho there, neighborino', {
48 * 'length': 24,
49 * 'separator': ' '
50 * });
51 * // => 'hi-diddly-ho there,...'
52 *
53 * _.truncate('hi-diddly-ho there, neighborino', {
54 * 'length': 24,
55 * 'separator': /,? +/
56 * });
57 * // => 'hi-diddly-ho there...'
58 *
59 * _.truncate('hi-diddly-ho there, neighborino', {
60 * 'omission': ' [...]'
61 * });
62 * // => 'hi-diddly-ho there, neig [...]'
63 */
64function truncate(string, options) {
65 var length = DEFAULT_TRUNC_LENGTH,
66 omission = DEFAULT_TRUNC_OMISSION;
67
68 if (isObject(options)) {
69 var separator = 'separator' in options ? options.separator : separator;
70 length = 'length' in options ? toInteger(options.length) : length;
71 omission = 'omission' in options ? toString(options.omission) : omission;
72 }
73 string = toString(string);
74
75 var strLength = string.length;
76 if (reHasComplexSymbol.test(string)) {
77 var strSymbols = stringToArray(string);
78 strLength = strSymbols.length;
79 }
80 if (length >= strLength) {
81 return string;
82 }
83 var end = length - stringSize(omission);
84 if (end < 1) {
85 return omission;
86 }
87 var result = strSymbols
88 ? strSymbols.slice(0, end).join('')
89 : string.slice(0, end);
90
91 if (separator === undefined) {
92 return result + omission;
93 }
94 if (strSymbols) {
95 end += (result.length - end);
96 }
97 if (isRegExp(separator)) {
98 if (string.slice(end).search(separator)) {
99 var match,
100 substring = result;
101
102 if (!separator.global) {
103 separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');
104 }
105 separator.lastIndex = 0;
106 while ((match = separator.exec(substring))) {
107 var newEnd = match.index;
108 }
109 result = result.slice(0, newEnd === undefined ? end : newEnd);
110 }
111 } else if (string.indexOf(separator, end) != end) {
112 var index = result.lastIndexOf(separator);
113 if (index > -1) {
114 result = result.slice(0, index);
115 }
116 }
117 return result + omission;
118}
119
120export default truncate;