// 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,
    // 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 : true,
    // should columns float left or right
    float : "left",
    // ensure the last column is never the tallest column
    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>'); // 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){
      while($parentColumn.height() < height &&
          $pullOutHere[0].childNodes.length){
        $putInHere.append($pullOutHere[0].childNodes[0]);
      }
      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);
          $putInHere.append(latestTextNode);
          
          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){
        $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){
              // 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: 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);
        // 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() {
      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){
        return singleColumnizeIt();
      }
      if($inBox.data("columnizing")) return;
      $inBox.data("columnized", true);
      $inBox.data("columnizing", true);
      
      $inBox.empty();
      // tbeck change
      // $inBox.append($("<div style='width:" + (Math.round(100 / numCols) - 2)+ "%; padding: 3px; float: " + options.float + ";'></div>")); //"
      $inBox.append($("<div style='width:" + (Math.round(100 / numCols) - 1)+ "%; 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){
          // jquery in ie6 can't clone with true
          $destroyable = $cache.clone();
        }
        $destroyable.css("visibility", "hidden");
        // 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;
          // tbeck change
          // $inBox.append($("<div class='" + className + "' style='width:" + (Math.round(100 / numCols) - 2)+ "%; float: " + options.float + ";'></div>")); //"
          $inBox.append($("<div class='" + className + "' style='width:" + (Math.round(100 / numCols) - 1)+ "%; float: " + options.float + ";'></div>")); //"
          
        }
        
        // fill all but the last column (unless overflowing)
        var i = 0;
        while(i < numCols - (options.overflow ? 0 : 1) || scrollHorizontally && $destroyable.children().length){
          if($inBox.children().length <= i){
            // we ran out of columns, make another
            // tbeck change
            // $inBox.append($("<div class='" + className + "' style='width:" + (Math.round(100 / numCols) - 2)+ "%; float: " + options.float + ";'></div>")); //"
            $inBox.append($("<div class='" + className + "' style='width:" + (Math.round(100 / numCols) - 1)+ "%; float: " + options.float + ";'></div>")); //"
          }
          var $col = $inBox.children().eq(i);
          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);
          }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");
            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);

