
	/**
	 * jQuery plugin: truncate.
	 *
	 * This plugin truncates texts that goes on several lines in order to display
	 * them on one line, with an end marker ('...', typically).
	 * If the resize plugin is enabled, it can also truncates texts dynamically when
	 * the window or even the text is resized.
	 * 
	 * The plugin tries to truncate on non-words locations (ex: "this is a text..." 
	 * instead of "this is a te..."). By specifying the value 1 to wrapZone, the text
	 * is truncated at the maximum width.
	 * 
	 * The options are:
	 * - endMarker: a string containing the end marker, '...' by default,
	 * - wrapZone: the width in percent of the total width (from 0 to 1) where the words
	 * 				should be cut if necessary (minimum width, actually), 0.8 by default,
	 * - onResize: indicates whether the text should be truncated on resize (if the resize plugin
	 * 				exists), true by default,
	 * - displayOnOver: indicates whether the full text should be displayed when the mouse
	 * 				is over the truncated text, true by default.
	 */
	(function($){
		$.fn.truncate = function(options) {
			settings = jQuery.extend({
				endMarker: '...',
				wrapZone: 0.8,
				onResize: true,
				displayOnOver: true
			}, options);

			this.each(function() {
				this._innerTextCopy = $(this).text();
			});
			var truncateFunction = function(event, w, h, ow, oh) {
				if (this._isMouseOver) return;
				var self = $(this);
				var thisDom = this;
				var text = this._innerTextCopy;
				var length = 0;

				var isOnOneLine = false;
				var currentText = self.text();
				var isAlreadyOnOneLine = false;
				self.text('A');
				var minHeight = thisDom.scrollHeight;
				self.css('overflow', 'hidden');
				self.css('height', minHeight);
				self.text(text);
				if (thisDom.scrollHeight > minHeight) {
					var hasMaxChars = false;
					var maxLength = (w < ow? currentText.length: text.length);
					while (!hasMaxChars) {
						var oldLength = length;
						length += (maxLength - length) / 2;
						self.text(text.substring(0, Math.round(length)) + settings.endMarker);
						if (thisDom.scrollHeight > minHeight) {
							maxLength = length;
							length = oldLength;
						} else if ((maxLength - length) < 1) {
							hasMaxChars = true;
						}
					}

					length = Math.round(length);
					var truncatedText = text.substring(0, Math.round(length));
					var lastBlankIndex = truncatedText.lastIndexOf(' ');
					if (lastBlankIndex > truncatedText.length * settings.wrapZone) {
						truncatedText = truncatedText.substring(0, lastBlankIndex);
					}
					self.text(truncatedText + settings.endMarker);
				}
			};
			if (settings.displayOnOver) {
				this.mouseover(function() {
					var self = $(this);
					self.css('overflow', '');
					self.css('height', '');
					self.text(this._innerTextCopy);
					this._isMouseOver = true;
				});
				this.mouseout(function() {
					this._isMouseOver = false;
					$(this).each(truncateFunction);
				});
			}
			if ($.fn.resize && settings.onResize) {
				this.resize(truncateFunction).resize();
			} else {
				this.each(truncateFunction);
			}
			return this;
		};
	})(jQuery);
