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