(function($) {

    // Matches trailing non-space characters.
    var chop = /(\s*\S+|\s)$/;
  
    // Matches the first word in the string.
    var start = /^(\S*)/;
  
    // Return a truncated html string.  Delegates to $.fn.truncate.
    $.truncate = function(html, options) {
      return $('<div></div>').append(html).truncate(options).html();
    };
  
    // Truncate the contents of an element in place.
    $.fn.truncate = function(options) {
      if ($.isNumeric(options)) options = {length: options};
      var o = $.extend({}, $.truncate.defaults, options);
  
      return this.each(function() {
        var self = $(this);
  
        if (o.noBreaks) self.find('br').replaceWith(' ');
  
        var text = self.text();
        var excess = text.length - o.length;
  
        if (o.stripTags) self.text(text);
  
        // Chop off any partial words if appropriate.
        if (o.words && excess > 0) {
          var truncated = text.slice(0, o.length).replace(chop, '').length;
  
          if (o.keepFirstWord && truncated === 0) {
            excess = text.length - start.exec(text)[0].length - 1;
          } else {
            excess = text.length - truncated - 1;
          }
        }
  
        if (excess < 0 || !excess && !o.truncated) return;
  
        // Iterate over each child node in reverse, removing excess text.
        $.each(self.contents().get().reverse(), function(i, el) {
          var $el = $(el);
          var text = $el.text();
          var length = text.length;
  
          // If the text is longer than the excess, remove the node and continue.
          if (length <= excess) {
            o.truncated = true;
            excess -= length;
            $el.remove();
            return;
          }
  
          // Remove the excess text and append the ellipsis.
          if (el.nodeType === 3) {
            // should we finish the block anyway?
            if (o.finishBlock) {
              $(el.splitText(length)).replaceWith(o.ellipsis);
            } else {
              $(el.splitText(length - excess - 1)).replaceWith(o.ellipsis);
            }
            return false;
          }
  
          // Recursively truncate child nodes.
          $el.truncate($.extend(o, {length: length - excess}));
          return false;
        });
      });
    };
  
    $.truncate.defaults = {
  
      // Strip all html elements, leaving only plain text.
      stripTags: false,
  
      // Only truncate at word boundaries.
      words: false,
  
      // When 'words' is active, keeps the first word in the string
      // even if it's longer than a target length.
      keepFirstWord: false,
  
      // Replace instances of <br> with a single space.
      noBreaks: false,
  
      // if true always truncate the content at the end of the block.
      finishBlock: false,
  
      // The maximum length of the truncated html.
      length: Infinity,
  
      // The character to use as the ellipsis.  The word joiner (U+2060) can be
      // used to prevent a hanging ellipsis, but displays incorrectly in Chrome
      // on Windows 7.
      // http://code.google.com/p/chromium/issues/detail?id=68323
      ellipsis: '\u2026' // '\u2060\u2026'
  
    };
  
  })(jQuery);