


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

(function(jQuery){

 jQuery.fn.columnize = function(options) {


	var defaults = {
		// default width of columnx
		width: 400,
		// optional # of columns instead of width
		columns : true,
		// true to build columns once regardless of window resize
		// false to rebuild when content box changes bounds
		buildOnce : true,
		// 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 : true,
		// should columns float left or right
		float : "left",
		// ensure the last column is never the tallest column
		lastNeverTallest : true
	};
	var options = jQuery.extend(defaults, options);

    return this.each(function() {
	    var jQueryinBox = options.target ? jQuery(options.target) : jQuery(this);
		var maxHeight = jQuery(this).height();
		var jQuerycache = jQuery('<div></div>'); // this is where we'll put the real content
		var lastWidth = 0;
		var columnizing = false;
		jQuerycache.append(jQuery(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(!jQueryinBox.data("imageLoaded")){
		    	jQueryinBox.data("imageLoaded", true);
		    	if(jQuery(this).find("img").length > 0){
		    		// only bother if there are
		    		// actually images...
			    	var func = function(jQueryinBox,jQuerycache){ return function(){
				    	if(!jQueryinBox.data("firstImageLoaded")){
				    		jQueryinBox.data("firstImageLoaded", "true");
					    	jQueryinBox.empty().append(jQuerycache.children().clone(true));
					    	jQueryinBox.columnize(options);
				    	}
			    	}}(jQuery(this), jQuerycache);
				    jQuery(this).find("img").one("load", func);
				    jQuery(this).find("img").one("abort", func);
				    return;
		    	}
	    	}
	    }
	    
		jQueryinBox.empty();
		
		columnizeIt();
		
		if(!options.buildOnce){
			jQuery(window).resize(function() {
				if(!options.buildOnce && jQuery.browser.msie){
					if(jQueryinBox.data("timeout")){
						clearTimeout(jQueryinBox.data("timeout"));
					}
					jQueryinBox.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
		 * @jQuerypullOutHere, a jQuery element
		 */
		function columnize(jQueryputInHere, jQuerypullOutHere, jQueryparentColumn, height){
			while(jQueryparentColumn.height() < height &&
				  jQuerypullOutHere[0].childNodes.length){
				jQueryputInHere.append(jQuerypullOutHere[0].childNodes[0]);
			}
			if(jQueryputInHere[0].childNodes.length == 0) return;
			
			// now we're too tall, undo the last one
			var kids = jQueryputInHere[0].childNodes;
			var lastKid = kids[kids.length-1];
			jQueryputInHere[0].removeChild(lastKid);
			var jQueryitem = jQuery(lastKid);
			
			
			if(jQueryitem[0].nodeType == 3){
				// it's a text node, split it up
				var oText = jQueryitem[0].nodeValue;
				var counter2 = options.width / 18;
				if(options.accuracy)
				counter2 = options.accuracy;
				var columnText;
				var latestTextNode = null;
				while(jQueryparentColumn.height() < height && oText.length){
					if (oText.indexOf(' ', counter2) != '-1') {
						columnText = oText.substring(0, oText.indexOf(' ', counter2));
					} else {
						columnText = oText;
					}
					latestTextNode = document.createTextNode(columnText);
					jQueryputInHere.append(latestTextNode);
					
					if(oText.length > counter2){
						oText = oText.substring(oText.indexOf(' ', counter2));
					}else{
						oText = "";
					}

				}
				if(jQueryparentColumn.height() >= height && latestTextNode != null){
					// too tall :(
					jQueryputInHere[0].removeChild(latestTextNode);
					oText = latestTextNode.nodeValue + oText;
				}
				if(oText.length){
					jQueryitem[0].nodeValue = oText;
				}else{
					return false; // we ate the whole text node, move on to the next node
				}
			}
			
			if(jQuerypullOutHere.children().length){
				jQuerypullOutHere.prepend(jQueryitem);
			}else{
				jQuerypullOutHere.append(jQueryitem);
			}
			
			return jQueryitem[0].nodeType == 3;
		}
		
		function split(jQueryputInHere, jQuerypullOutHere, jQueryparentColumn, height){
			if(jQuerypullOutHere.children().length){
				jQuerycloneMe = jQuerypullOutHere.children(":first");
				jQueryclone = jQuerycloneMe.clone(true);
				if(jQueryclone.attr("nodeType") == 1 && !jQueryclone.hasClass("dontend")){ 
					jQueryputInHere.append(jQueryclone);
					if(jQueryclone.is("img") && jQueryparentColumn.height() < height + 20){
						jQuerycloneMe.remove();
					}else if(!jQuerycloneMe.hasClass("dontsplit") && jQueryparentColumn.height() < height + 20){
						jQuerycloneMe.remove();
					}else if(jQueryclone.is("img") || jQuerycloneMe.hasClass("dontsplit")){
						jQueryclone.remove();
					}else{
						jQueryclone.empty();
						if(!columnize(jQueryclone, jQuerycloneMe, jQueryparentColumn, height)){
							if(jQuerycloneMe.children().length){
								split(jQueryclone, jQuerycloneMe, jQueryparentColumn, height);
							}
						}
						if(jQueryclone.get(0).childNodes.length == 0){
							// it was split, but nothing is in it :(
							jQueryclone.remove();
						}
					}
				}
			}
		}
		
		
		function singleColumnizeIt() {
			if (jQueryinBox.data("columnized") && jQueryinBox.children().length == 1) {
				return;
			}
			jQueryinBox.data("columnized", true);
			jQueryinBox.data("columnizing", true);
			
			jQueryinBox.empty();
			jQueryinBox.append(jQuery("<div class='first last pjcolumn' style='width:100%; float: " + options.float + ";'></div>")); //"
			jQuerycol = jQueryinBox.children().eq(jQueryinBox.children().length-1);
			jQuerydestroyable = jQuerycache.clone(true);
			if(options.overflow){
				targetHeight = options.overflow.height;
				columnize(jQuerycol, jQuerydestroyable, jQuerycol, targetHeight);
				// make sure that the last item in the column isn't a "dontend"
				if(!jQuerydestroyable.children().find(":first-child").hasClass("dontend")){
					split(jQuerycol, jQuerydestroyable, jQuerycol, targetHeight);
				}
				
				while(checkDontEndColumn(jQuerycol.children(":last").length && jQuerycol.children(":last").get(0))){
					var jQuerylastKid = jQuerycol.children(":last");
					jQuerylastKid.remove();
					jQuerydestroyable.prepend(jQuerylastKid);
				}

				var html = "";
				var div = document.createElement('DIV');
				while(jQuerydestroyable[0].childNodes.length > 0){
					var kid = jQuerydestroyable[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(jQuerydestroyable[0].childNodes[0]);
					html += div.innerHTML;
				}
				var overflow = jQuery(options.overflow.id)[0];
				overflow.innerHTML = html;

			}else{
				jQuerycol.append(jQuerydestroyable);
			}
			jQueryinBox.data("columnizing", false);
			
			if(options.overflow){
				options.overflow.doneFunc();
			}
			
		}
		
		function checkDontEndColumn(dom){
			if(dom.nodeType != 1) return false;
			if(jQuery(dom).hasClass("dontend")) return true;
			if(dom.childNodes.length == 0) return false;
			return checkDontEndColumn(dom.childNodes[dom.childNodes.length-1]);
		}
		
		function columnizeIt() {
			if(lastWidth == jQueryinBox.width()) return;
			lastWidth = jQueryinBox.width();
			
			var numCols = Math.round(jQueryinBox.width() / options.width);
			if(options.columns) numCols = options.columns;
//			if (jQueryinBox.data("columnized") && numCols == jQueryinBox.children().length) {
//				return;
//			}
			if(numCols <= 1){
				return singleColumnizeIt();
			}
			if(jQueryinBox.data("columnizing")) return;
			jQueryinBox.data("columnized", true);
			jQueryinBox.data("columnizing", true);
			
			jQueryinBox.empty();
			jQueryinBox.append(jQuery("<div style='width:" + (Math.round(10000 / numCols)/100)+ "%; float: " + options.float + ";'></div>")); //"
			jQuerycol = jQueryinBox.children(":last");
			jQuerycol.append(jQuerycache.clone());
			maxHeight = jQuerycol.height();
			jQueryinBox.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;
			}
			
			for(var loopCount=0;loopCount<maxLoops;loopCount++){
				jQueryinBox.empty();
				var jQuerydestroyable;
				try{
					jQuerydestroyable = jQuerycache.clone(true);
				}catch(e){
					// jquery in ie6 can't clone with true
					jQuerydestroyable = jQuerycache.clone();
				}
				jQuerydestroyable.css("visibility", "hidden");
				// create the columns
				for (var i = 0; i < numCols; i++) {
					/* create column */
					var className = (i == 0) ? "first pjcolumn" : "pjcolumn";
					var className = (i == numCols - 1) ? ("last " + className) : className;
					jQueryinBox.append(jQuery("<div class='" + className + "' style='width:" + (Math.round(10000 / numCols)/100.2)+ "%; float: " + options.float + ";'></div>")); //"
				}
				
				// fill all but the last column (unless overflowing)
				var i = 0;
				while(i < numCols - (options.overflow ? 0 : 1) || scrollHorizontally && jQuerydestroyable.children().length){
					if(jQueryinBox.children().length <= i){
						// we ran out of columns, make another
						jQueryinBox.append(jQuery("<div class='" + className + "' style='width:" + (Math.round(10000 / numCols)/100.2)+ "%; float: " + options.float + ";'></div>")); //"
					}
					var jQuerycol = jQueryinBox.children().eq(i);
					columnize(jQuerycol, jQuerydestroyable, jQuerycol, targetHeight);
					// make sure that the last item in the column isn't a "dontend"
					if(!jQuerydestroyable.children().find(":first-child").hasClass("dontend")){
						split(jQuerycol, jQuerydestroyable, jQuerycol, targetHeight);
					}else{
//						alert("not splitting a dontend");
					}
					
					while(checkDontEndColumn(jQuerycol.children(":last").length && jQuerycol.children(":last").get(0))){
						var jQuerylastKid = jQuerycol.children(":last");
						jQuerylastKid.remove();
						jQuerydestroyable.prepend(jQuerylastKid);
					}
					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(jQuerydestroyable[0].childNodes.length > 0){
							var kid = jQuerydestroyable[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(jQuerydestroyable[0].childNodes[0]);
							html += div.innerHTML;
						}
						var overflow = jQuery(options.overflow.id)[0];
						overflow.innerHTML = html;
					}else{
						jQuery(options.overflow.id).empty().append(jQuerydestroyable.children().clone(true));
					}
				}else if(!scrollHorizontally){
					// the last column in the series
					jQuerycol = jQueryinBox.children().eq(jQueryinBox.children().length-1);
					while(jQuerydestroyable.children().length) jQuerycol.append(jQuerydestroyable.children(":first"));
					var afterH = jQuerycol.height();
					var diff = afterH - targetHeight;
					var totalH = 0;
					var min = 10000000;
					var max = 0;
					var lastIsMax = false;
					jQueryinBox.children().each(function(jQueryinBox){ return function(jQueryitem){
						var h = jQueryinBox.children().eq(jQueryitem).height();
						lastIsMax = false;
						totalH += h;
						if(h > max) {
							max = h;
							lastIsMax = true;
						}
						if(h < min) min = h;
					}}(jQueryinBox));

					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
					jQueryinBox.children().each(function(i){
						jQuerycol = jQueryinBox.children().eq(i);
						jQuerycol.width(options.width + "px");
						if(i==0){
							jQuerycol.addClass("first");
						}else if(i==jQueryinBox.children().length-1){
							jQuerycol.addClass("last");
						}else{
							jQuerycol.removeClass("first");
							jQuerycol.removeClass("last");
						}
					});
					jQueryinBox.width(jQueryinBox.children().length * options.width + "px");
				}
				jQueryinBox.append(jQuery("<br style='clear:both;'>"));
			}
			jQueryinBox.find('.pjcolumn').find(':first.removeiffirst').remove();
			jQueryinBox.find('.pjcolumn').find(':last.removeiflast').remove();
			jQueryinBox.data("columnizing", false);

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


