(function($){
	$.fn.truncate = function( max, settings ) {
		settings = $.extend( {
			chars: /\s/,
			trail: [ "...", "" ]
		}, settings );
		
		var ie = $.browser.msie;
		function fixIE( o ) {
			if ( ie ) {
				o.style.removeAttribute( "filter" );
			}
		}
		
		var parseSubChilds = function(element, maxLength) {
			var element = $(element),
				html = element.html()
					.replace(/ +/, ' ')
					.replace(/\r\n\t/,'')
					.replace(/^ +/, '')
					.replace(/ +$/, ''),
				childs, 
				b = true, i = 0, c = 0, count = 0;
			
			element.html(html);
			childs = element[0].childNodes;
			
			b = true;
			for (i=0; i < childs.length; i++) {
				if (!b) {
					element[0].removeChild(childs[i]);
					continue;
				}
				
				if (childs[i].nodeType == 3) {		//text node
					text = $(childs[i]).text().replace(/\s+/g,' ');
					c = text.length;
					if ( (count+c) > maxLength ) {
						text = text.substring(0, maxLength - count);
						text = text.substring(0, text.lastIndexOf(" "));
						childs[i].nodeValue = text;
						
						count += text.length;
						b = false;
					} else {
						count += c;
					}
				}
				else if (childs[i].nodeType == 1) {	//element node
					if (!parseSubChilds(childs[i], maxLength - count)) {
						text = $(childs[i]).text().replace(/\s+/g,' ');
						count += text.length;
						b = false;
					}
				}
			}
			return b;
		}

		var parseChilds = function(element, maxLength){
			var element = $(element);
			var childs = element.children();
			var count = 0, c = 0;
			var stop = false;
			
			for (i=0; i < childs.length; i++) {
				if (stop) {
					$(childs[i]).remove();
					continue;
				}
				
				c = $(childs[i]).text().replace(/\s+/g,' ').length;
				
				if ((count+c) > maxLength) {
					parseSubChilds(childs[i], maxLength - count);
					stop = true;
				}
				
				count +=c ;
			}
		};
		
		
		return this.each( function() {
			var el = $(this),
				originalHTML = el.html(),
				ellipsisHTML = el.html();
				
			parseChilds(el, max);
			ellipsisHTML = el.html();
			
			el.html($("<div />").addClass("truncate_less").append(el.html()).append(settings.trail[ 0 ]));
			
			$(".truncate_show", el).click(function(event) {
				if ( $(".truncate_more", el).length < 1) {
					el.append($("<div />").addClass("truncate_more").css("display", "none").append(originalHTML).append(settings.trail[ 1 ]));
					
					$(".truncate_hide", el).click(function(event) {
						$(".truncate_more", el).css( "background", "#fff" ).fadeOut( 0, function() {
							$(".truncate_less", el).css( "background", "#fff" ).fadeIn( 0, function() {
                                fixIE( this );
                                $(this).css( "background", "none" );
                            });
                            fixIE( this );
                        });
                        return false;
					});
				}
				
				
				$( ".truncate_less", el ).fadeOut( 0, function() {
                    $( ".truncate_more", el ).fadeIn( 0, function() {
                        fixIE( this );
                    });
                    fixIE( this );
                });
				
				
                $(".truncate_show", el).click( function() {
                    $( ".truncate_less", el ).css( "background", "#fff" ).fadeOut( 0, function() {
                        $( ".truncate_more", el ).css( "background", "#fff" ).fadeIn( 0, function() {
                            fixIE( this );
                            $(this).css( "background", "none" );
                        });
                        fixIE( this );
                    });
                    return false;
                });
                return false;
				
			});
		});
	};
})(jQuery);
