/*
Columnize Plugin for jQuery
Version: v0.10

Copyright (C) 2008-2010 by Lutz Issler

Systemantics GmbH
Am Lavenstein 3
52064 Aachen
GERMANY

Web:    www.systemantics.net
Email:  hello@systemantics.net

This plugin is distributed under the terms of the
GNU Lesser General Public license. The license can be obtained
from http://www.gnu.org/licenses/lgpl.html.

*/

(function() {
    var cloneEls = new Object();
    var numColsById = new Object();
    var uniqueId = 0;

    function _layoutElement(elDOM, settings, balance) {
        // Some semi-global variables
        var colHeight;
        var colWidth;
        var col;
        var currentColEl;
        var cols = new Array();
        var colNum = 0;
        var colSet = 0;

        var el = jQuery(elDOM);

        // Save numCols property for this element
        // (needed for pagination)
        numColsById[elDOM.id] = settings.columns;

        // Remove child nodes
        el.empty();

        // Macro function (with side effects)
        function _newColumn() {
            colNum++;

            // Add a new column
            col = document.createElement("DIV");
            col.className = settings.column;
            el.append(col);
            currentColEl = col;
            colWidth = jQuery(col).width();
            cols.push(col);

            // Add the same subnode nesting to the new column
            // as there was in the old column
            for (var j = 0; j < subnodes.length; j++) {
                newEl = subnodes[j].cloneNode(false);
                if (j == 0 || innerContinued) {
                    jQuery(newEl).addClass(settings.continued);
                }
                currentColEl.appendChild(newEl);
                currentColEl = newEl;
            }
        }

        // Returns the margin-bottom CSS property of a certain node
        function _getMarginBottom(currentColEl) {
            var marginBottom = parseInt(jQuery(currentColEl).css("marginBottom"));
            if (marginBottom.toString() == 'NaN') {
                marginBottom = 0;
            }
            var currentColElParents = jQuery(currentColEl).parents();
            for (var j = 0; j < currentColElParents.length; j++) {
                if (currentColElParents[j] == elDOM) {
                    break;
                }
                var curMarginBottom = parseInt(jQuery(currentColElParents[j]).css("marginBottom"));
                if (curMarginBottom.toString() != 'NaN') {
                    marginBottom = Math.max(marginBottom, curMarginBottom);
                }
            }
            return marginBottom;
        }

        // Advance to next sibling on el or a parent level
        function _skipToNextNode() {
            while (currentEl && currentColEl && !currentEl.nextSibling) {
                currentEl = currentEl.parentNode;
                currentColEl = currentColEl.parentNode;
                var node = subnodes.pop();
                // Hack: delete the previously saved HREF
                if (node == "A") {
                    href = null;
                }
            }
            if (currentEl) {
                currentEl = currentEl.nextSibling;
            }
        }

        // Take the height from the element to be layouted
        var maxHeight = settings.height
			? settings.height
			: parseInt(el.css("maxHeight"));
        if (balance || isNaN(maxHeight) || maxHeight == 0) {
            // We are asked to balance the col lengths
            // or cannot get the column length from the container,
            // so chose a height that will produce >numCols< columns
            col = document.createElement("DIV");
            col.className = settings.column;
            jQuery(col).append(jQuery(cloneEls[elDOM.id]).html());
            el.append(col);
            var lineHeight = parseInt(el.css("lineHeight"));
            if (!lineHeight) {
                // Assume a line height of 120%
                lineHeight = Math.ceil(parseInt(el.css("fontSize")) * 1.2);
            }
            colHeight = Math.ceil(jQuery(col).height() / settings.columns);
            if (colHeight % lineHeight > 0) {
                colHeight += lineHeight;
            }
            elDOM.removeChild(col);
            if (maxHeight > 0 && colHeight > maxHeight) {
                // Balance only to max-height
                colHeight = maxHeight;
            }
        } else {
            colHeight = maxHeight;
        }

        // Take the minimum height into account
        var minHeight = settings.minHeight
			? settings.minHeight
			: parseInt(el.css("minHeight"));
        if (minHeight) {
            colHeight = Math.max(colHeight, minHeight);
        }

        // Start with first child of the initial node
        var currentEl = cloneEls[elDOM.id].children(":first")[0];
        var subnodes = new Array();
        var href = null;
        var lastNodeType = 0;
        _newColumn();
        if (colHeight == 0 || colWidth == 0) {
            // We cannot continue with zero height or width
            return false;
        }
        while (currentEl) {
            if (currentEl.nodeType == 1) {
                // An element node
                var newEl;
                var $currentEl = jQuery(currentEl);
                if ($currentEl.hasClass("dontSplit")
					|| $currentEl.is(settings.dontsplit)) {
                    // Don't split this node. Instead, clone it completely
                    var newEl = currentEl.cloneNode(true);
                    currentColEl.appendChild(newEl);
                    if (col.offsetHeight > colHeight) {
                        // The column gets too long, start a new colum
                        _newColumn();
                    }
                    _skipToNextNode();
                } else {
                    // Clone the node and append it to the current column
                    var newEl = currentEl.cloneNode(false);
                    currentColEl.appendChild(newEl);
                    if (col.offsetHeight - _getMarginBottom(currentColEl) > colHeight) {
                        // The column gets too long, start a new colum
                        currentColEl.removeChild(newEl);
                        var toBeInsertedEl = newEl;
                        _newColumn();
                        currentColEl.appendChild(toBeInsertedEl);
                        newEl = toBeInsertedEl;
                    }
                    if (currentEl.firstChild) {
                        subnodes.push(currentEl.cloneNode(false));
                        currentColEl = newEl;
                        currentEl = currentEl.firstChild;
                    } else {
                        _skipToNextNode();
                    }
                }
                lastNodeType = 1;
            } else if (currentEl.nodeType == 3) {
                // A text node
                var newEl = document.createTextNode("");
                currentColEl.appendChild(newEl);
                // Determine the current bottom margin
                var marginBottom = _getMarginBottom(currentColEl);
                // Append word by word
                var words = currentEl.data.split(" ");
                for (var i = 0; i < words.length; i++) {
                    if (lastNodeType == 3) {
                        newEl.appendData(" ");
                    }
                    newEl.appendData(words[i]);
                    currentColEl.removeChild(newEl);
                    currentColEl.appendChild(newEl);
                    if (col.offsetHeight - marginBottom > colHeight) {
                        // el column is full
                        // Remove the last word
                        newEl.data = newEl.data.substr(0, newEl.data.length - words[i].length - 1);

                        // Remove the last node if empty
                        var innerContinued;
                        if (jQuery(currentColEl).text() == "") {
                            jQuery(currentColEl).remove();
                            innerContinued = false;
                        } else {
                            innerContinued = true;
                        }

                        // Start a new column
                        _newColumn();

                        // Add a text node at the bottom level
                        // in order to continue the column
                        newEl = document.createTextNode(words[i]);
                        currentColEl.appendChild(newEl);
                    }
                    lastNodeType = 3;
                }
                _skipToNextNode();
                lastNodeType = 0;
            } else {
                // Any other node (comments, for instance)
                _skipToNextNode();
                lastNodeType = currentEl.nodeType;
            }
        }
        return cols;
    };

    jQuery.fn.columnize = function(settings) {
        settings = jQuery.extend({
            column: "column",
            continued: "continued",
            columns: 2,
            balance: true,
            height: false,
            minHeight: false,
            cache: true,
            dontsplit: ""
        }, settings);
        this.each(function() {
            var jthis = jQuery(this);

            var id = this.id;
            if (!id) {
                // Get a new id
                id = "jcols_" + uniqueId;
                this.id = id;
                uniqueId++;
            }

            if (!cloneEls[this.id] || !settings.cache) {
                cloneEls[this.id] = jthis.clone(true);
            }

            // Layout the columns
            var cols = _layoutElement(this, settings, settings.balance);
            if (!cols) {
                // Layout failed, restore the object's contents
                jthis.append(cloneEls[this.id].children().clone(true));
            }
        });
        return this;
    }
})();

