/*
 *  TODO:
 *    - offset in parent
 *    - append text after non text node removal
 */

function abridge(target, root, rootHeight, rootWidth, options) {
	if ((jQuery(root).outerHeight() <= rootHeight) && (jQuery(root).outerWidth() <= rootWidth)) return (target != root);
	if (target.nodeType == 3) { // text node
		var elem = jQuery(target).parent();
		var fullText = jQuery(target).text();
		var appendHtml = options.appendHtml;
		var elemContentsCount = elem.contents().size();
		var reserve = options.reserve;
		var start = 0;
		var end = fullText.length - 1;
		while (start < end) {
			var len = Math.ceil((end - start)/2) + start;
			elem.contents().last().replaceWith(document.createTextNode(fullText.substr(0, len)));
			elem.append(appendHtml);
//			alert("" + start + "   " + end + "   " + len);
			if (jQuery(root).outerHeight() > rootHeight || jQuery(root).outerWidth() > rootWidth)
				end -= Math.ceil((end - start) / 2);
			else
				start += Math.ceil((end - start) / 2);
			elem.contents().filter(function(index) { return index >= elemContentsCount; }).remove();
		}
		var finalText = fullText.substr(0, end - reserve);
		var spaceIndex;
		if (options.breakOnWordBoundary) {
			spaceIndex = finalText.lastIndexOf(' ');
		} else {
			spaceIndex = fullText.length - 1;
		}
		while (spaceIndex > 0 && options.trimChars.indexOf(finalText.charAt(spaceIndex-1)) != -1) spaceIndex--;
		if (fullText.charAt(end-reserve) != ' ' && spaceIndex > 0) finalText = finalText.substr(0, spaceIndex);
		if (finalText) {
			elem.contents().last().replaceWith(document.createTextNode(finalText));
			elem.append(appendHtml);
		} else elem.contents().last().remove();
	} else { // non text node
		var contents = jQuery(target).contents();
		if (contents.size() == 0) { // has no children
			jQuery(target).remove();
		} else { // has children
			var backup = null;
			while (((contents = jQuery(target).contents()).size() > 0) && (jQuery(root).outerHeight() > rootHeight || jQuery(root).outerWidth() > rootWidth)) {
				backup = contents.eq(-1).detach();
			}
			if (jQuery(root).outerHeight() > rootHeight || jQuery(root).outerWidth() > rootWidth) {
				jQuery(target).remove();
			} else {
				jQuery(target).append(backup);
				return abridge(jQuery(target).contents().eq(-1).get(0), root, rootHeight, rootWidth, options);
			}
		}
	}
	return true;
}

jQuery.fn.abridge = function(options) {
	var defaults = {
		appendHtml: "...",
		trimChars: " .",
		reserve: 0,
		breakOnWordBoundary: true
	}
	return this.each(function() {
		var effectiveOptions = jQuery.extend(defaults, options);
		var height = 0;
		var width = 0;
		if (options.height == undefined || options.width == undefined) {
			var parent = jQuery(this).parent();
			var bodyElem = jQuery('body').get(0);
			while (parent.get(0) != bodyElem && parent.css('overflow') != 'hidden') {
				parent = parent.parent();
			}
			height = options.height ? options.height : parent.height();
			width = options.width ? options.width : parent.width();
		} else {
			height = options.height;
			width = options.width;
		}
		abridge(this, this, height, width, effectiveOptions);
	});
}