// version 1.4.0
// http://welcome.totheinter.net/columnizer-jquery-plugin/
// created by: Adam Wulf adam.wulf@gmail.com

(function($){

 $.fn.columnize = function(options) {


	var defaults = {
		// default width of columnx
		width: 400,
		// default padding
		padding: "0 3 0 3",
		// optional # of columns instead of width
		columns : false,
		// true to build columns once regardless of window resize
		// false to rebuild when content box changes bounds
		buildOnce : false,
		// an object with options if the text should overflow
		// it's container if it can't fit within a specified height
		overflow : false,
		// this function is called after content is columnized
		doneFunc : function(){},
		// if the content should be columnized into a 
		// container node other than it's own node
		target : false,
		// re-columnizing when images reload might make things
		// run slow. so flip this to true if it's causing delays
		ignoreImageLoading : false,
		// should columns float left or right
		float : "left",
		// ensure the last column is never the tallest column
		lastNeverTallest : false,
		// content has block
		hasBlock: false,
		// block minimum height at the footer of column
		blockMinHeightFoo: 1,
		// block minimum height at the bar of column
		blockMinHeightBar: 1,
		// block has some columns of parent witdh
		inAnycaseManyColomns: false,
		// private properties
		needsplit: true,
		insplitnow: false,
		maxColumnHeightNow: false
	};
	var options = $.extend(defaults, options);

    return this.each(function() {
	    var $inBox = options.target ? $(options.target) : $(this);
		var maxHeight = $(this).height();
		var $cache = $('<div></div>'); // this is where we'll put the real content
		var lastWidth = 0;
		var columnizing = false;
		$cache.append($(this).children().clone(true));
	    
	    // images loading after dom load
	    // can screw up the column heights,
	    // so recolumnize after images load
	    if(!options.ignoreImageLoading && !options.target){
	    	if(!$inBox.data("imageLoaded")){
		    	$inBox.data("imageLoaded", true);
		    	if($(this).find("img").length > 0){
		    		// only bother if there are
		    		// actually images...
			    	var func = function($inBox,$cache){ return function(){
				    	if(!$inBox.data("firstImageLoaded")){
				    		$inBox.data("firstImageLoaded", "true");
					    	$inBox.empty().append($cache.children().clone(true));
					    	$inBox.columnize(options);

				    	}
			    	}}($(this), $cache);
				    $(this).find("img").one("load", func);
				    $(this).find("img").one("abort", func);
				    return;
		    	}
	    	}
	    }
	    
		$inBox.empty();
		
		columnizeIt();
		
		if(!options.buildOnce){
			$(window).resize(function() {
				if(!options.buildOnce && $.browser.msie){
					if($inBox.data("timeout")){
						clearTimeout($inBox.data("timeout"));
					}
					$inBox.data("timeout", setTimeout(columnizeIt, 200));
				}else if(!options.buildOnce){
					columnizeIt();
				}else{
					// don't rebuild
				}
			});
		}
		
		/**
		 * return a node that has a height
		 * less than or equal to height
		 *
		 * @param putInHere, a dom element
		 * @$pullOutHere, a jQuery element
		 */
		function columnize($putInHere, $pullOutHere, $parentColumn, height){
/*
alert(
$parentColumn.height() + "\n" + height 
+ "\n" +				  $pullOutHere[0].childNodes.length
);
*/
			/**
			 * если мы в сплите и есть опция то проверяем а нет ли у нас установки на другую высоту
			 */
			if (options.hasBlock && options.insplitnow && options.maxColumnHeightNow) {
				height = options.maxColumnHeightNow;
			}

			options.needsplit = true;
			var previous_height = 0, new_height = 0;
			while (($parentColumn.height() < height) && $pullOutHere[0].childNodes.length) {
				//alert($pullOutHere[0].childNodes[0]);
				//alert($pullOutHere[0].childNodes[0]);
				$putInHere.append($pullOutHere[0].childNodes[0]);
				new_height = $parentColumn.height();
				if (options.hasBlock && !options.insplitnow && (height < new_height)) {
					//alert(new_height + '::' + previous_height + '::' + (new_height - previous_height) + '::' + (height - previous_height) + '::' + height);
					if ((previous_height + options.blockMinHeightFoo) > height) {
						// если минимальная стартовая высота не влазиет, переносим блок в новую колонку
						options.needsplit = false;
					} else if ((new_height - previous_height) < options.blockMinHeightFoo + options.blockMinHeightBar) {
						// если сам блок меньше чем сумма минимальных высот, переносим блок в новую колонку
						options.needsplit = false;
					} else {
						var block_height = new_height - previous_height
						  , maxBlockHeightInTheStartColumn = block_height - options.blockMinHeightBar;
						if (previous_height + maxBlockHeightInTheStartColumn < height) {
							// меняем максимальную высоту, чтобы остаток был нормальной высоты в новой колонке
							options.maxColumnHeightNow = previous_height + maxBlockHeightInTheStartColumn;
						} else {
							options.maxColumnHeightNow = false;
						}
					}
				}
				
				if (options.insplitnow) {
					//alert(new_height + '::' + previous_height + '::' + (new_height - previous_height) + '::' + height);
				}

				previous_height = new_height;
			}

			if ($putInHere[0].childNodes.length == 0) {
				return;
			}

			// now we're too tall, undo the last one
			var kids = $putInHere[0].childNodes;
			var lastKid = kids[kids.length-1];
			$putInHere[0].removeChild(lastKid);
			var $item = $(lastKid);

			if($item[0].nodeType == 3){
				// it's a text node, split it up
				var oText = $item[0].nodeValue;
				var counter2 = options.width / 18;
				if (options.accuracy) {
					counter2 = options.accuracy;
				}

				var columnText;
				var latestTextNode = null;
				while($parentColumn.height() <= height && oText.length){
					if (oText.indexOf(' ', counter2) != '-1') {
						columnText = oText.substring(0, oText.indexOf(' ', counter2));
					} else {
						columnText = oText;
					}

					latestTextNode = document.createTextNode(columnText);
					//alert(columnText);
					$putInHere.append(latestTextNode);
					//alert($parentColumn.height() + '::' + height);
					if(oText.length > counter2){
						oText = oText.substring(oText.indexOf(' ', counter2));
					}else{
						oText = "";
					}
				}
				if($parentColumn.height() >= height && latestTextNode != null){
					// too tall :(
					$putInHere[0].removeChild(latestTextNode);
					oText = latestTextNode.nodeValue + oText;
				}
				if(oText.length){
					$item[0].nodeValue = oText;
				}else{
					return false; // we ate the whole text node, move on to the next node
				}
			}
			if($pullOutHere.children().length || !options.needsplit){
				$pullOutHere.prepend($item);
			}else{
				$pullOutHere.append($item);
			}
			
			return $item[0].nodeType == 3;
		}
		
		function split($putInHere, $pullOutHere, $parentColumn, height){
			if (options.hasBlock && options.insplitnow && options.maxColumnHeightNow) {
				height = options.maxColumnHeightNow;
			}

			var temp_height = 20;
			if (options.hasBlock) {
				temp_height = 0;
			}

			if($pullOutHere.children().length){
				$cloneMe = $pullOutHere.children(":first");
				$clone = $cloneMe.clone(true);
				if($clone.attr("nodeType") == 1 && !$clone.hasClass("dontend")){ 
					$putInHere.append($clone);
					if($clone.is("img") && $parentColumn.height() < height + temp_height){
						$cloneMe.remove();
					}else if(!$cloneMe.hasClass("dontsplit") && $parentColumn.height() < height + temp_height){
						$cloneMe.remove();
					}else if($clone.is("img") || $cloneMe.hasClass("dontsplit")){
						$clone.remove();
					}else{
						$clone.empty();
						if(!columnize($clone, $cloneMe, $parentColumn, height)){
							if($cloneMe.children().length){
								split($clone, $cloneMe, $parentColumn, height);
							}
						}
						if($clone.get(0).childNodes.length == 0){
							// it was split, but nothing is in it :(
							$clone.remove();
						}
					}
				}
			}
		}
		
		
		function singleColumnizeIt() {
			if ($inBox.data("columnized") && $inBox.children().length == 1) {
				return;
			}
			$inBox.data("columnized", true);
			$inBox.data("columnizing", true);

			$inBox.empty();
			$inBox.append($("<div class='first last column' style='/*width:98%;*/ padding: " + options.padding + "; float: " + options.float + ";'></div>")); //"
			$col = $inBox.children().eq($inBox.children().length-1);
			$destroyable = $cache.clone(true);
			if(options.overflow){
				targetHeight = options.overflow.height;
				columnize($col, $destroyable, $col, targetHeight);
				// make sure that the last item in the column isn't a "dontend"
				if(!$destroyable.children().find(":first-child").hasClass("dontend")){
					split($col, $destroyable, $col, targetHeight);
				}
				
				while(checkDontEndColumn($col.children(":last").length && $col.children(":last").get(0))){
					var $lastKid = $col.children(":last");
					$lastKid.remove();
					$destroyable.prepend($lastKid);
				}

				var html = "";
				var div = document.createElement('DIV');
				while($destroyable[0].childNodes.length > 0){
					var kid = $destroyable[0].childNodes[0];
					for(var i=0;i<kid.attributes.length;i++){
						if(kid.attributes[i].nodeName.indexOf("jQuery") == 0){
							kid.removeAttribute(kid.attributes[i].nodeName);
						}
					}
					div.innerHTML = "";
					div.appendChild($destroyable[0].childNodes[0]);
					html += div.innerHTML;
				}
				var overflow = $(options.overflow.id)[0];
				overflow.innerHTML = html;

			}else{
				$col.append($destroyable);
			}
			$inBox.data("columnizing", false);
			
			if(options.overflow){
				options.overflow.doneFunc();
			}
			
		}
		
		function checkDontEndColumn(dom){
			if(dom.nodeType != 1) return false;
			if($(dom).hasClass("dontend")) return true;
			if(dom.childNodes.length == 0) return false;
			return checkDontEndColumn(dom.childNodes[dom.childNodes.length-1]);
		}
		
		function columnizeIt() {
//alert($inBox.get(0).scrollWidth);
			if(lastWidth == $inBox.width()) return;
			lastWidth = $inBox.width();
			var numCols = Math.round($inBox.width() / options.width);
			if(options.columns) numCols = options.columns;
//			if ($inBox.data("columnized") && numCols == $inBox.children().length) {
//				return;
//			}
			if((numCols <= 1) && !options.inAnycaseManyColomns){
				return singleColumnizeIt();
			}
			
			if($inBox.data("columnizing")) return;
			$inBox.data("columnized", true);
			$inBox.data("columnizing", true);
			
			$inBox.empty();
			var width_display = (Math.round(100 / numCols) - 2) + '%';
			if (options.width) {
				width_display = options.width + 'px';
			}

			$inBox.append($("<div style='width:" + width_display + "; padding: " + options.padding + "; float: " + options.float + ";'></div>")); //"
			$col = $inBox.children(":last");
			$col.append($cache.clone());
			maxHeight = $col.height();
			$inBox.empty();
			
			var targetHeight = maxHeight / numCols;
			var firstTime = true;
			var maxLoops = 3;
			var scrollHorizontally = false;
			if(options.overflow){
				maxLoops = 1;
				targetHeight = options.overflow.height;
			}else if(options.height && options.width){
				maxLoops = 1;
				targetHeight = options.height;
				scrollHorizontally = true;
				//numCols = Math.ceil(maxHeight / targetHeight);
			}
			for(var loopCount=0;loopCount<maxLoops;loopCount++){
				$inBox.empty();
				var $destroyable;
				try{
					$destroyable = $cache.clone(true);
				}catch(e){
					// jquery in ie6 can't clone with true
					$destroyable = $cache.clone();
				}
				$destroyable.css("visibility", "hidden");
				var addit_hie = '';
				if (false) {
					addit_hie = "//height: " + targetHeight + "px;";
				}
				// create the columns
				for (var i = 0; i < numCols; i++) {
					/* create column */
					var className = (i == 0) ? "first column" : "column";
					var className = (i == numCols - 1) ? ("last " + className) : className;
					$inBox.append($("<div class='" + className + "' style='width:" + width_display+ "; padding:"+ options.padding + "; float: " + options.float + ";" + addit_hie + "'></div>")); //"
				}
				
				// fill all but the last column (unless overflowing)
				var i = 0;
				while(i < numCols - ((options.overflow || options.inAnycaseManyColomns) ? 0 : 1) || (options.columns - 1) == i || !options.columns && scrollHorizontally && $destroyable.children().length) {
					if($inBox.children().length <= i){
						// we ran out of columns, make another
						$inBox.append($("<div class='" + className + "' style='width:" + width_display+ "; padding:"+ options.padding + "; float: " + options.float + ";" + addit_hie + "'></div>")); //"
					}
					var $col = $inBox.children().eq(i);
options.maxColumnHeightNow = targetHeight;
options.insplitnow = false;
					columnize($col, $destroyable, $col, targetHeight);
options.insplitnow = true;
if (options.needsplit) {
					// make sure that the last item in the column isn't a "dontend"
					if(!$destroyable.children().find(":first-child").hasClass("dontend")){
						split($col, $destroyable, $col, targetHeight);
						if ( options.columns && i == (options.columns - 1) ) {
							var kids = $col[0].childNodes;
							if ( kids.length ) {
								var lastKid = kids[kids.length-1];
								$col[0].removeChild(lastKid);
							}
						}
					}else{
//						alert("not splitting a dontend");
					}
					
					while(checkDontEndColumn($col.children(":last").length && $col.children(":last").get(0))){
						var $lastKid = $col.children(":last");
						$lastKid.remove();
						$destroyable.prepend($lastKid);
					}
}

					i++;
				}
				if(options.overflow && !scrollHorizontally){
					var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;
					var IE7 = (document.all) && (navigator.appVersion.indexOf("MSIE 7.") != -1);
					if(IE6 || IE7){
						var html = "";
						var div = document.createElement('DIV');
						while($destroyable[0].childNodes.length > 0){
							var kid = $destroyable[0].childNodes[0];
							for(var i=0;i<kid.attributes.length;i++){
								if(kid.attributes[i].nodeName.indexOf("jQuery") == 0){
									kid.removeAttribute(kid.attributes[i].nodeName);
								}
							}
							div.innerHTML = "";
							div.appendChild($destroyable[0].childNodes[0]);
							html += div.innerHTML;
						}
						var overflow = $(options.overflow.id)[0];
						overflow.innerHTML = html;
					}else{
						$(options.overflow.id).empty().append($destroyable.children().clone(true));
					}
				}else if(!scrollHorizontally){
					// the last column in the series
					$col = $inBox.children().eq($inBox.children().length-1);
					while($destroyable.children().length) $col.append($destroyable.children(":first"));
					var afterH = $col.height();
					var diff = afterH - targetHeight;
					var totalH = 0;
					var min = 10000000;
					var max = 0;
					var lastIsMax = false;
					$inBox.children().each(function($inBox){ return function($item){
						var h = $inBox.children().eq($item).height();
						lastIsMax = false;
						totalH += h;
						if(h > max) {
							max = h;
							lastIsMax = true;
						}
						if(h < min) min = h;
					}}($inBox));

					var avgH = totalH / numCols;
					if(options.lastNeverTallest && lastIsMax){
						// the last column is the tallest
						// so allow columns to be taller
						// and retry
						targetHeight = targetHeight + 30;
						if(loopCount == maxLoops-1) maxLoops++;
					}else if(max - min > 30){
						// too much variation, try again
						targetHeight = avgH + 30;
					}else if(Math.abs(avgH-targetHeight) > 20){
						// too much variation, try again
						targetHeight = avgH;
					}else {
						// solid, we're done
						loopCount = maxLoops;
					}
				}else{
					// it's scrolling horizontally, fix the width/classes of the columns
					$inBox.children().each(function(i){
						$col = $inBox.children().eq(i);
						$col.width(options.width + "px");
						// @author lucifer
						$col.css("height", targetHeight + 'px');
						//$col.css("height", '100%');
						//$col.height(targetHeight);
						// \@author lucifer
						if(i==0){
							$col.addClass("first");
						}else if(i==$inBox.children().length-1){
							$col.addClass("last");
						}else{
							$col.removeClass("first");
							$col.removeClass("last");
						}
					});
					//$inBox.width($inBox.children().length * options.width + "px");
				}
				$inBox.append($("<br style='clear:both;'>"));
			}
			$inBox.find('.column').find(':first.removeiffirst').remove();
			$inBox.find('.column').find(':last.removeiflast').remove();
			$inBox.data("columnizing", false);

			if(options.overflow){
				options.overflow.doneFunc();
			}
			options.doneFunc();
		}
    });
 };
})(jQuery);

