(function($) {
	$.fn.columnize = function(options) {
		var defaults = {
			width : 400,
			columns : false,
			buildOnce : false,
			overflow : false,
			doneFunc : function() {
			},
			target : false,
			ignoreImageLoading : true,
			float : "left",
			lastNeverTallest : true
		};
		
		var options = $.extend(defaults, options);
		return this
				.each(function() {
					var $inBox = options.target ? $(options.target) : $(this);
					var maxHeight = $(this).height();
					var $cache = $('<div></div>');
					var lastWidth = 0;
					var columnizing = false;
					$cache.append($(this).children().clone(true));
					if (!options.ignoreImageLoading && !options.target) {
						if (!$inBox.data("imageLoaded")) {
							$inBox.data("imageLoaded", true);
							if ($(this).find("img").length > 0) {
								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 {
											}
										});
					}
					function columnize($putInHere, $pullOutHere, $parentColumn,
							height) {
						while ($parentColumn.height() < height
								&& $pullOutHere[0].childNodes.length) {
							$putInHere.append($pullOutHere[0].childNodes[0]);
						}
						if ($putInHere[0].childNodes.length == 0)
							return;
						var kids = $putInHere[0].childNodes;
						var lastKid = kids[kids.length - 1];
						$putInHere[0].removeChild(lastKid);
						var $item = $(lastKid);
						if ($item[0].nodeType == 3) {
							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);
								$putInHere.append(latestTextNode);
								if (oText.length > counter2) {
									oText = oText.substring(oText.indexOf(' ',
											counter2));
								} else {
									oText = "";
								}
							}
							if ($parentColumn.height() >= height
									&& latestTextNode != null) {
								$putInHere[0].removeChild(latestTextNode);
								oText = latestTextNode.nodeValue + oText;
							}
							if (oText.length) {
								$item[0].nodeValue = oText;
							} else {
								return false;
							}
						}
						if ($pullOutHere.children().length) {
							$pullOutHere.prepend($item);
						} else {
							$pullOutHere.append($item);
						}
						return $item[0].nodeType == 3;
					}
					function split($putInHere, $pullOutHere, $parentColumn,
							height) {
						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 + 20) {
									$cloneMe.remove();
								} else if (!$cloneMe.hasClass("dontsplit")
										&& $parentColumn.height() < height + 20) {
									$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) {
										$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: 3px; 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);
							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() {
						if (lastWidth == $inBox.width())
							return;
						lastWidth = $inBox.width();
						var numCols = Math
								.round($inBox.width() / options.width);
						if (options.columns)
							numCols = options.columns;
						if (numCols <= 1) {
							return singleColumnizeIt();
						}
						if ($inBox.data("columnizing"))
							return;
						$inBox.data("columnized", true);
						$inBox.data("columnizing", true);
						$inBox.empty();
						$inBox.append($("<div style='width:"
								+ (Math.round(100 / numCols) - 2)
								+ "%; padding: 3px; 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;
						}
						for ( var loopCount = 0; loopCount < maxLoops; loopCount++) {
							$inBox.empty();
							var $destroyable;
							try {
								$destroyable = $cache.clone(true);
							} catch (e) {
								$destroyable = $cache.clone();
							}
							$destroyable.css("visibility", "hidden");
							for ( var i = 0; i < numCols; i++) {
								var className = (i == 0) ? "first column"
										: "column";
								var className = (i == numCols - 1) ? ("last " + className)
										: className;
								$inBox.append($("<div class='" + className
										+ "' style='width:"
										+ (Math.round(100 / numCols) - 2)
										+ "%; float: " + options.float
										+ ";'></div>"));
							}
							var i = 0;
							while (i < numCols - (options.overflow ? 0 : 1)
									|| scrollHorizontally
									&& $destroyable.children().length) {
								if ($inBox.children().length <= i) {
									$inBox.append($("<div class='" + className
											+ "' style='width:"
											+ (Math.round(100 / numCols) - 2)
											+ "%; float: " + options.float
											+ ";'></div>"));
								}
								var $col = $inBox.children().eq(i);
								columnize($col, $destroyable, $col,
										targetHeight);
								if (!$destroyable.children().find(
										":first-child").hasClass("dontend")) {
									split($col, $destroyable, $col,
											targetHeight);
								} else {
								}
								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;
								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) {
								$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) {
									targetHeight = targetHeight + 30;
									if (loopCount == maxLoops - 1)
										maxLoops++;
								} else if (max - min > 30) {
									targetHeight = avgH + 30;
								} else if (Math.abs(avgH - targetHeight) > 20) {
									targetHeight = avgH;
								} else {
									loopCount = maxLoops;
								}
							} else {
								$inBox
										.children()
										.each(
												function(i) {
													$col = $inBox.children()
															.eq(i);
													$col.width(options.width
															+ "px");
													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);
