(function ($) {
    $.fn.columnize = function (options) {
        var defaults = {
            width: 400,
            columns: false,
            buildOnce: false,
            overflow: false,
            doneFunc: function () {},
            target: false,
            ignoreImageLoading: true,
            float: "left",
            lastNeverTallest: 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>');
            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) - 6) + "%; 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) - 6) + "%; 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);
