/**! * trunk8 v1.3.2 * https://github.com/rviscomi/trunk8 * * Copyright 2012 Rick Viscomi * Released under the MIT License. * * Date: October 21, 2012 */ (function ($) { var methods, utils, SIDES = { /* cen...ter */ center: 'center', /* ...left */ left: 'left', /* right... */ right: 'right' }, WIDTH = { auto: 'auto' }; function trunk8(element) { this.$element = $(element); this.original_text = this.$element.html(); this.settings = $.extend({}, $.fn.trunk8.defaults); } trunk8.prototype.updateSettings = function (options) { this.settings = $.extend(this.settings, options); }; function truncate() { var data = this.data('trunk8'), settings = data.settings, width = settings.width, side = settings.side, fill = settings.fill, line_height = utils.getLineHeight(this) * settings.lines, str = data.original_text, length = str.length, max_bite = '', lower, upper, bite_size, bite; /* Reset the field to the original string. */ this.html(str); if (width === WIDTH.auto) { /* Assuming there is no "overflow: hidden". */ if (this.height() <= line_height) { /* Text is already at the optimal trunkage. */ return; } /* Binary search technique for finding the optimal trunkage. */ /* Find the maximum bite without overflowing. */ lower = 0; upper = length - 1; while (lower <= upper) { bite_size = lower + ((upper - lower) >> 1); bite = utils.eatStr(str, side, length - bite_size, fill); this.html(bite); /* Check for overflow. */ if (this.height() > line_height) { upper = bite_size - 1; } else { lower = bite_size + 1; /* Save the bigger bite. */ max_bite = (max_bite.length > bite.length) ? max_bite : bite; } } /* Reset the content to eliminate possible existing scroll bars. */ this.html(''); /* Display the biggest bite. */ this.html(max_bite); if (settings.tooltip) { this.attr('title', str); } } else if (!isNaN(width)) { bite_size = length - width; bite = utils.eatStr(str, side, bite_size, fill); this.html(bite); if (settings.tooltip) { this.attr('title', str); } } else { $.error('Invalid width "' + width + '".'); } } methods = { init: function (options) { return this.each(function () { var $this = $(this), data = $this.data('trunk8'); if (!data) { $this.data('trunk8', (data = new trunk8(this))); } data.updateSettings(options); truncate.call($this); }); }, /** Updates the text value of the elements while maintaining truncation. */ update: function (new_string) { return this.each(function () { var $this = $(this); /* Update text. */ if (new_string) { $this.data('trunk8').original_text = new_string; } /* Truncate accordingly. */ truncate.call($this); }); }, revert: function () { return this.each(function () { /* Get original text. */ var text = $(this).data('trunk8').original_text; /* Revert element to original text. */ $(this).html(text); }); }, /** Returns this instance's settings object. NOT CHAINABLE. */ getSettings: function () { return this.get(0).data('trunk8').settings; } }; utils = { /** Replaces [bite_size] [side]-most chars in [str] with [fill]. */ eatStr: function (str, side, bite_size, fill) { var length = str.length, key = utils.eatStr.generateKey.apply(null, arguments), half_length, half_bite_size; /* If the result is already in the cache, return it. */ if (utils.eatStr.cache[key]) { return utils.eatStr.cache[key]; } /* Common error handling. */ if ((typeof str !== 'string') || (length === 0)) { $.error('Invalid source string "' + str + '".'); } if ((bite_size < 0) || (bite_size > length)) { $.error('Invalid bite size "' + bite_size + '".'); } else if (bite_size === 0) { /* No bite should show no truncation. */ return str; } if (typeof (fill + '') !== 'string') { $.error('Fill unable to be converted to a string.'); } /* Compute the result, store it in the cache, and return it. */ switch (side) { case SIDES.right: /* str... */ return utils.eatStr.cache[key] = $.trim(str.substr(0, length - bite_size)) + fill; case SIDES.left: /* ...str */ return utils.eatStr.cache[key] = fill + $.trim(str.substr(bite_size)); case SIDES.center: /* Bit-shift to the right by one === Math.floor(x / 2) */ half_length = length >> 1; // halve the length half_bite_size = bite_size >> 1; // halve the bite_size /* st...r */ return utils.eatStr.cache[key] = $.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) + fill + $.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, '')); default: $.error('Invalid side "' + side + '".'); } }, getLineHeight: function (elem) { var $elem = $(elem), floats = $elem.css('float'), position = $elem.css('position'), html = $elem.html(), wrapper_id = 'line-height-test', line_height; if (floats !== 'none') { $elem.css('float', 'none'); } if (position === 'absolute') { $elem.css('position', 'static'); } /* Set the content to a small single character and wrap. */ $elem.html('i').wrap('
'); /* Calculate the line height by measuring the wrapper.*/ line_height = $('#' + wrapper_id).innerHeight(); /* Remove the wrapper and reset the style/content. */ $elem.html(html).css({ 'float': floats, 'position': position }).unwrap(); return line_height; } }; utils.eatStr.cache = {}; utils.eatStr.generateKey = function () { return Array.prototype.join.call(arguments, ''); }; $.fn.trunk8 = function (method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || !method) { return methods.init.apply(this, arguments); } else { $.error('Method ' + method + ' does not exist on jQuery.trunk8'); } }; /* Default trunk8 settings. */ $.fn.trunk8.defaults = { fill: '…', lines: 1, side: SIDES.right, tooltip: true, width: WIDTH.auto }; })(jQuery);