diff --git a/imageresize b/imageresize new file mode 100644 index 0000000..9bacc54 --- /dev/null +++ b/imageresize @@ -0,0 +1,630 @@ +// version 1.6.0 +// http://welcome.totheinter.net/columnizer-jquery-plugin/ +// created by: Adam Wulf @adamwulf, adam.wulf@gmail.com + +(function($){ + + $.fn.columnize = function(options) { + + + var defaults = { + // default width of columns + 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 + columnFloat : "left", + // ensure the last column is never the tallest column + lastNeverTallest : false, + // (int) the minimum number of characters to jump when splitting + // text nodes. smaller numbers will result in higher accuracy + // column widths, but will take slightly longer + accuracy : false, + // don't automatically layout columns, only use manual columnbreak + manualBreaks : false, + // prefix for all the CSS classes used by this plugin + // default to empty string for backwards compatibility + cssClassPrefix : "", + // place images in their own column if too large. + // works on images with class "images" appended. + imgarrange: false + }; + options = $.extend(defaults, options); + + if(typeof(options.width) == "string"){ + options.width = parseInt(options.width,10); + if(isNaN(options.width)){ + options.width = defaults.width; + } + } + + return this.each(function() { + var $inBox = options.target ? $(options.target) : $(this); + var maxHeight = $(this).height(); + var $cache = $('
'); // this is where we'll put the real content + var lastWidth = 0; + var columnizing = false; + var manualBreaks = options.manualBreaks; + var cssClassPrefix = defaults.cssClassPrefix; + var imgarrange = options.imgarrange; + if(typeof(options.cssClassPrefix) == "string"){ + cssClassPrefix = options.cssClassPrefix; + } + + + var adjustment = 0; + + $cache.append($(this).contents().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){ + if($inBox.data("timeout")){ + clearTimeout($inBox.data("timeout")); + } + $inBox.data("timeout", setTimeout(columnizeIt, 200)); + } + }); + } + + function prefixTheClassName(className, withDot){ + var dot = withDot ? "." : ""; + if(cssClassPrefix.length){ + return dot + cssClassPrefix + "-" + className; + } + return dot + className; + } + + /** + * this fuction builds as much of a column as it can without + * splitting nodes in half. If the last node in the new column + * is a text node, then it will try to split that text node. otherwise + * it will leave the node in $pullOutHere and return with a height + * smaller than targetHeight. + * + * Returns a boolean on whether we did some splitting successfully at a text point + * (so we know we don't need to split a real element). return false if the caller should + * split a node if possible to end this column. + * + * @param putInHere, the jquery node to put elements into for the current column + * @param $pullOutHere, the jquery node to pull elements out of (uncolumnized html) + * @param $parentColumn, the jquery node for the currently column that's being added to + * @param targetHeight, the ideal height for the column, get as close as we can to this height + */ + function columnize($putInHere, $pullOutHere, $parentColumn, targetHeight){ + // + // add as many nodes to the column as we can, + // but stop once our height is too tall + while((manualBreaks || $parentColumn.height() < targetHeight) && + $pullOutHere[0].childNodes.length){ + var nxnode = $pullOutHere[0].childNodes[1]; + var node = $pullOutHere[0].childNodes[0]; + if( imgarrange && $(nxnode) && $(nxnode).hasClass("images")) { + + // if next node is a large image, and you want to resize them + // to fit a column, then just break out so that it + // gets put on a column of its own + // fixes placement uncertainty with certain mobile devices + $parentColumn.addClass("columnbreak"); + $(nxnode).addClass("columnbreak"); + return; + } + + // + // Because we're not cloning, jquery will actually move the element" + // http://welcome.totheinter.net/2009/03/19/the-undocumented-life-of-jquerys-append/ + // + + if($(node).find(prefixTheClassName("columnbreak", true)).length){ + // + // our column is on a column break, so just end here + return; + } + if($(node).hasClass(prefixTheClassName("columnbreak"))){ + // + // our column is on a column break, so just end here + return; + } + + $putInHere.append(node); + } + if($putInHere[0].childNodes.length === 0) return; + + // now we're too tall, so undo the last one + var kids = $putInHere[0].childNodes; + var lastKid = kids[kids.length-1]; + $putInHere[0].removeChild(lastKid); + var $item = $(lastKid); + + // now lets try to split that last node + // to fit as much of it as we can into this column + 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() < targetHeight && oText.length){ + var indexOfSpace = oText.indexOf(' ', counter2); + if (indexOfSpace != -1) { + columnText = oText.substring(0, oText.indexOf(' ', counter2)); + } else { + columnText = oText; + } + latestTextNode = document.createTextNode(columnText); + $putInHere.append(latestTextNode); + + if(oText.length > counter2 && indexOfSpace != -1){ + oText = oText.substring(indexOfSpace); + }else{ + oText = ""; + } + } + if($parentColumn.height() >= targetHeight && 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.contents().length){ + $pullOutHere.prepend($item); + }else{ + $pullOutHere.append($item); + } + + return $item[0].nodeType == 3; + } + + /** + * Split up an element, which is more complex than splitting text. We need to create + * two copies of the element with it's contents divided between each + */ + function split($putInHere, $pullOutHere, $parentColumn, targetHeight){ + if($putInHere.contents(":last").find(prefixTheClassName("columnbreak", true)).length){ + // + // our column is on a column break, so just end here + return; + } + if($putInHere.contents(":last").hasClass(prefixTheClassName("columnbreak"))){ + // + // our column is on a column break, so just end here + return; + } + if($pullOutHere.contents().length){ + var $cloneMe = $pullOutHere.contents(":first"); + // + // make sure we're splitting an element + if($cloneMe.get(0).nodeType != 1){ + return; + } + // + // clone the node with all data and events + var $clone = $cloneMe.clone(true); + // + // need to support both .prop and .attr if .prop doesn't exist. + // this is for backwards compatibility with older versions of jquery. + + if($cloneMe.hasClass(prefixTheClassName("columnbreak"))){ + // + // ok, we have a columnbreak, so add it into + // the column and exit + $putInHere.append($clone); + $cloneMe.remove(); + }else if (manualBreaks){ + // keep adding until we hit a manual break + $putInHere.append($clone); + $cloneMe.remove(); + }else if($clone.get(0).nodeType == 1 && !$clone.hasClass(prefixTheClassName("dontend"))){ + $putInHere.append($clone); + if($clone.is("img") && $parentColumn.height() < targetHeight + 20){ + // + // we can't split an img in half, so just add it + // to the column and remove it from the pullOutHere section + $cloneMe.remove(); + }else if(!$cloneMe.hasClass(prefixTheClassName("dontsplit")) && $parentColumn.height() < targetHeight + 20){ + // + // pretty close fit, and we're not allowed to split it, so just + // add it to the column, remove from pullOutHere, and be done + $cloneMe.remove(); + }else if($clone.is("img") || $cloneMe.hasClass(prefixTheClassName("dontsplit"))){ + // + // it's either an image that's too tall, or an unsplittable node + // that's too tall. leave it in the pullOutHere and we'll add it to the + // next column + + $clone.remove(); + }else{ + // + // ok, we're allowed to split the node in half, so empty out + // the node in the column we're building, and start splitting + // it in half, leaving some of it in pullOutHere + $clone.empty(); + if(!columnize($clone, $cloneMe, $parentColumn, targetHeight)){ // && !$clone.is("img") + // this node still has non-text nodes to split + // add the split class and then recur + $cloneMe.addClass(prefixTheClassName("split")); + if($cloneMe.children().length){ + split($clone, $cloneMe, $parentColumn, targetHeight); + } + }else{ + // this node only has text node children left, add the + // split class and move on. + $cloneMe.addClass(prefixTheClassName("split")); + } + 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($("
")); + $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.contents().find(":first-child").hasClass(prefixTheClassName("dontend"))){ + split($col, $destroyable, $col, targetHeight); + } + + while($col.contents(":last").length && checkDontEndColumn($col.contents(":last").get(0))){ + var $lastKid = $col.contents(":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]; + if(kid.attributes){ + for(var i=0;i<= 1){ + return singleColumnizeIt(); + } + if($inBox.data("columnizing")) return; + $inBox.data("columnized", true); + $inBox.data("columnizing", true); + + $inBox.empty(); + $inBox.append($("
")); //" + $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(optionHeight && optionWidth){ + maxLoops = 1; + targetHeight = optionHeight; + scrollHorizontally = true; + } + + // + // We loop as we try and workout a good height to use. We know it initially as an average + // but if the last column is higher than the first ones (which can happen, depending on split + // points) we need to raise 'adjustment'. We try this over a few iterations until we're 'solid'. + // + // also, lets hard code the max loops to 20. that's /a lot/ of loops for columnizer, + // and should keep run aways in check. if somehow someone has content combined with + // options that would cause an infinite loop, then this'll definitely stop it. + for(var loopCount=0;loopCount<20;loopCount++){ + $inBox.empty(); + var $destroyable, className, $col, $lastKid; + 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 */ + className = (i === 0) ? prefixTheClassName("first") : ""; + className += " " + prefixTheClassName("column"); + className = (i == numCols - 1) ? (prefixTheClassName("last") + " " + className) : className; + $inBox.append($("
")); //" + } + + // fill all but the last column (unless overflowing) + i = 0; + while(i < numCols - (options.overflow ? 0 : 1) || scrollHorizontally && $destroyable.contents().length){ + if($inBox.children().length <= i){ + // we ran out of columns, make another + $inBox.append($("
")); //" + } + $col = $inBox.children().eq(i); + if(scrollHorizontally){ + $col.width(optionWidth + "px"); + } + columnize($col, $destroyable, $col, targetHeight); + // make sure that the last item in the column isn't a "dontend" + split($col, $destroyable, $col, targetHeight); + + while($col.contents(":last").length && checkDontEndColumn($col.contents(":last").get(0))){ + $lastKid = $col.contents(":last"); + $lastKid.remove(); + $destroyable.prepend($lastKid); + } + i++; + + // + // https://github.com/adamwulf/Columnizer-jQuery-Plugin/issues/47 + // + // check for infinite loop. + // + // this could happen when a dontsplit or dontend item is taller than the column + // we're trying to build, and its never actually added to a column. + // + // this results in empty columns being added with the dontsplit item + // perpetually waiting to get put into a column. lets force the issue here + if($col.contents().length === 0 && $destroyable.contents().length){ + // + // ok, we're building zero content columns. this'll happen forever + // since nothing can ever get taken out of destroyable. + // + // to fix, lets put 1 item from destroyable into the empty column + // before we iterate + $col.append($destroyable.contents(":first")); + }else if(i == numCols - (options.overflow ? 0 : 1) && !options.overflow){ + // + // ok, we're about to exit the while loop because we're done with all + // columns except the last column. + // + // if $destroyable still has columnbreak nodes in it, then we need to keep + // looping and creating more columns. + if($destroyable.find(prefixTheClassName("columnbreak", true)).length){ + numCols ++; + } + } + } + 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(i=0;i max) { + max = h; + lastIsMax = true; + } + if(h < min) min = h; + numberOfColumnsThatDontEndInAColumnBreak++; + } + }; + }($inBox)); + + var avgH = totalH / numberOfColumnsThatDontEndInAColumnBreak; + if(totalH === 0){ + // + // all columns end in a column break, + // so we're done here + loopCount = maxLoops; + }else if(options.lastNeverTallest && lastIsMax){ + // the last column is the tallest + // so allow columns to be taller + // and retry + // + // hopefully this'll mean more content fits into + // earlier columns, so that the last column + // can be shorter than the rest + adjustment += 30; + + 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(optionWidth + "px"); + if(i === 0){ + $col.addClass(prefixTheClassName("first")); + }else if(i==$inBox.children().length-1){ + $col.addClass(prefixTheClassName("last")); + }else{ + $col.removeClass(prefixTheClassName("first")); + $col.removeClass(prefixTheClassName("last")); + } + }); + $inBox.width($inBox.children().length * optionWidth + "px"); + } + $inBox.append($("
")); + } + $inBox.find(prefixTheClassName("column", true)).find(":first" + prefixTheClassName("removeiffirst", true)).remove(); + $inBox.find(prefixTheClassName("column", true)).find(':last' + prefixTheClassName("removeiflast", true)).remove(); + $inBox.data("columnizing", false); + + if(options.overflow){ + options.overflow.doneFunc(); + } + options.doneFunc(); + } + }); + }; +})(jQuery);