1 var isObject = require('./isObject'),
2 isRegExp = require('./isRegExp'),
3 stringSize = require('./internal/stringSize'),
4 stringToArray = require('./internal/stringToArray'),
5 toInteger = require('./toInteger'),
6 toString = require('./toString');
8 /** Used as default options for `_.truncate`. */
9 var DEFAULT_TRUNC_LENGTH = 30,
10 DEFAULT_TRUNC_OMISSION = '...';
12 /** Used to match `RegExp` flags from their coerced string values. */
15 /** Used to compose unicode character classes. */
16 var rsAstralRange = '\\ud800-\\udfff',
17 rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
18 rsComboSymbolsRange = '\\u20d0-\\u20f0',
19 rsVarRange = '\\ufe0e\\ufe0f';
21 /** Used to compose unicode capture groups. */
22 var rsZWJ = '\\u200d';
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/). */
25 var reHasComplexSymbol = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
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 "...".
35 * @param {string} [string=''] The string to truncate.
36 * @param {Object} [options] The options object.
37 * @param {number} [options.length=30] The maximum string length.
38 * @param {string} [options.omission='...'] The string to indicate text is omitted.
39 * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
40 * @returns {string} Returns the truncated string.
43 * _.truncate('hi-diddly-ho there, neighborino');
44 * // => 'hi-diddly-ho there, neighbo...'
46 * _.truncate('hi-diddly-ho there, neighborino', {
50 * // => 'hi-diddly-ho there,...'
52 * _.truncate('hi-diddly-ho there, neighborino', {
56 * // => 'hi-diddly-ho there...'
58 * _.truncate('hi-diddly-ho there, neighborino', {
59 * 'omission': ' [...]'
61 * // => 'hi-diddly-ho there, neig [...]'
63 function truncate(string, options) {
64 var length = DEFAULT_TRUNC_LENGTH,
65 omission = DEFAULT_TRUNC_OMISSION;
67 if (isObject(options)) {
68 var separator = 'separator' in options ? options.separator : separator;
69 length = 'length' in options ? toInteger(options.length) : length;
70 omission = 'omission' in options ? toString(options.omission) : omission;
72 string = toString(string);
74 var strLength = string.length;
75 if (reHasComplexSymbol.test(string)) {
76 var strSymbols = stringToArray(string);
77 strLength = strSymbols.length;
79 if (length >= strLength) {
82 var end = length - stringSize(omission);
86 var result = strSymbols
87 ? strSymbols.slice(0, end).join('')
88 : string.slice(0, end);
90 if (separator === undefined) {
91 return result + omission;
94 end += (result.length - end);
96 if (isRegExp(separator)) {
97 if (string.slice(end).search(separator)) {
101 if (!separator.global) {
102 separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');
104 separator.lastIndex = 0;
105 while ((match = separator.exec(substring))) {
106 var newEnd = match.index;
108 result = result.slice(0, newEnd === undefined ? end : newEnd);
110 } else if (string.indexOf(separator, end) != end) {
111 var index = result.lastIndexOf(separator);
113 result = result.slice(0, index);
116 return result + omission;
119 module.exports = truncate;