[25999] | 1 | // version 1.6.0 |
---|
| 2 | // http://welcome.totheinter.net/columnizer-jquery-plugin/ |
---|
| 3 | // created by: Adam Wulf @adamwulf, adam.wulf@gmail.com |
---|
| 4 | |
---|
| 5 | (function($){ |
---|
| 6 | |
---|
| 7 | $.fn.columnize = function(options) { |
---|
| 8 | |
---|
| 9 | |
---|
| 10 | var defaults = { |
---|
| 11 | // default width of columns |
---|
| 12 | width: 400, |
---|
| 13 | // optional # of columns instead of width |
---|
| 14 | columns : false, |
---|
| 15 | // true to build columns once regardless of window resize |
---|
| 16 | // false to rebuild when content box changes bounds |
---|
| 17 | buildOnce : false, |
---|
| 18 | // an object with options if the text should overflow |
---|
| 19 | // it's container if it can't fit within a specified height |
---|
| 20 | overflow : false, |
---|
| 21 | // this function is called after content is columnized |
---|
| 22 | doneFunc : function(){}, |
---|
| 23 | // if the content should be columnized into a |
---|
| 24 | // container node other than it's own node |
---|
| 25 | target : false, |
---|
| 26 | // re-columnizing when images reload might make things |
---|
| 27 | // run slow. so flip this to true if it's causing delays |
---|
| 28 | ignoreImageLoading : true, |
---|
| 29 | // should columns float left or right |
---|
| 30 | columnFloat : "left", |
---|
| 31 | // ensure the last column is never the tallest column |
---|
| 32 | lastNeverTallest : false, |
---|
| 33 | // (int) the minimum number of characters to jump when splitting |
---|
| 34 | // text nodes. smaller numbers will result in higher accuracy |
---|
| 35 | // column widths, but will take slightly longer |
---|
| 36 | accuracy : false, |
---|
| 37 | // don't automatically layout columns, only use manual columnbreak |
---|
| 38 | manualBreaks : false, |
---|
| 39 | // previx for all the CSS classes used by this plugin |
---|
| 40 | // default to empty string for backwards compatibility |
---|
| 41 | cssClassPrefix : "" |
---|
| 42 | }; |
---|
| 43 | options = $.extend(defaults, options); |
---|
| 44 | |
---|
| 45 | if(typeof(options.width) == "string"){ |
---|
| 46 | options.width = parseInt(options.width,10); |
---|
| 47 | if(isNaN(options.width)){ |
---|
| 48 | options.width = defaults.width; |
---|
| 49 | } |
---|
| 50 | } |
---|
| 51 | |
---|
| 52 | /** |
---|
| 53 | * appending a text node to a <table> will |
---|
| 54 | * cause a jquery crash. |
---|
| 55 | * so wrap all append() calls and revert to |
---|
| 56 | * a simple appendChild() in case it fails |
---|
| 57 | */ |
---|
| 58 | function appendSafe($target, $elem){ |
---|
| 59 | try{ |
---|
| 60 | $target.append($elem); |
---|
| 61 | }catch(e){ |
---|
| 62 | $target[0].appendChild($elem[0]); |
---|
| 63 | } |
---|
| 64 | } |
---|
| 65 | |
---|
| 66 | return this.each(function() { |
---|
| 67 | var $inBox = options.target ? $(options.target) : $(this); |
---|
| 68 | var maxHeight = $(this).height(); |
---|
| 69 | var $cache = $('<div></div>'); // this is where we'll put the real content |
---|
| 70 | var lastWidth = 0; |
---|
| 71 | var columnizing = false; |
---|
| 72 | var manualBreaks = options.manualBreaks; |
---|
| 73 | var cssClassPrefix = defaults.cssClassPrefix; |
---|
| 74 | if(typeof(options.cssClassPrefix) == "string"){ |
---|
| 75 | cssClassPrefix = options.cssClassPrefix; |
---|
| 76 | } |
---|
| 77 | |
---|
| 78 | |
---|
| 79 | var adjustment = 0; |
---|
| 80 | |
---|
| 81 | appendSafe($cache, $(this).contents().clone(true)); |
---|
| 82 | |
---|
| 83 | // images loading after dom load |
---|
| 84 | // can screw up the column heights, |
---|
| 85 | // so recolumnize after images load |
---|
| 86 | if(!options.ignoreImageLoading && !options.target){ |
---|
| 87 | if(!$inBox.data("imageLoaded")){ |
---|
| 88 | $inBox.data("imageLoaded", true); |
---|
| 89 | if($(this).find("img").length > 0){ |
---|
| 90 | // only bother if there are |
---|
| 91 | // actually images... |
---|
| 92 | var func = function($inBox,$cache){ return function(){ |
---|
| 93 | if(!$inBox.data("firstImageLoaded")){ |
---|
| 94 | $inBox.data("firstImageLoaded", "true"); |
---|
| 95 | appendSafe($inBox.empty(), $cache.children().clone(true)); |
---|
| 96 | $inBox.columnize(options); |
---|
| 97 | } |
---|
| 98 | }; |
---|
| 99 | }($(this), $cache); |
---|
| 100 | $(this).find("img").one("load", func); |
---|
| 101 | $(this).find("img").one("abort", func); |
---|
| 102 | return; |
---|
| 103 | } |
---|
| 104 | } |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | $inBox.empty(); |
---|
| 108 | |
---|
| 109 | columnizeIt(); |
---|
| 110 | |
---|
| 111 | if(!options.buildOnce){ |
---|
| 112 | $(window).resize(function() { |
---|
| 113 | if(!options.buildOnce){ |
---|
| 114 | if($inBox.data("timeout")){ |
---|
| 115 | clearTimeout($inBox.data("timeout")); |
---|
| 116 | } |
---|
| 117 | $inBox.data("timeout", setTimeout(columnizeIt, 200)); |
---|
| 118 | } |
---|
| 119 | }); |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | function prefixTheClassName(className, withDot){ |
---|
| 123 | var dot = withDot ? "." : ""; |
---|
| 124 | if(cssClassPrefix.length){ |
---|
| 125 | return dot + cssClassPrefix + "-" + className; |
---|
| 126 | } |
---|
| 127 | return dot + className; |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | /** |
---|
| 131 | * this fuction builds as much of a column as it can without |
---|
| 132 | * splitting nodes in half. If the last node in the new column |
---|
| 133 | * is a text node, then it will try to split that text node. otherwise |
---|
| 134 | * it will leave the node in $pullOutHere and return with a height |
---|
| 135 | * smaller than targetHeight. |
---|
| 136 | * |
---|
| 137 | * Returns a boolean on whether we did some splitting successfully at a text point |
---|
| 138 | * (so we know we don't need to split a real element). return false if the caller should |
---|
| 139 | * split a node if possible to end this column. |
---|
| 140 | * |
---|
| 141 | * @param putInHere, the jquery node to put elements into for the current column |
---|
| 142 | * @param $pullOutHere, the jquery node to pull elements out of (uncolumnized html) |
---|
| 143 | * @param $parentColumn, the jquery node for the currently column that's being added to |
---|
| 144 | * @param targetHeight, the ideal height for the column, get as close as we can to this height |
---|
| 145 | */ |
---|
| 146 | function columnize($putInHere, $pullOutHere, $parentColumn, targetHeight){ |
---|
| 147 | // |
---|
| 148 | // add as many nodes to the column as we can, |
---|
| 149 | // but stop once our height is too tall |
---|
| 150 | while((manualBreaks || $parentColumn.height() < targetHeight) && |
---|
| 151 | $pullOutHere[0].childNodes.length){ |
---|
| 152 | var node = $pullOutHere[0].childNodes[0]; |
---|
| 153 | // |
---|
| 154 | // Because we're not cloning, jquery will actually move the element" |
---|
| 155 | // http://welcome.totheinter.net/2009/03/19/the-undocumented-life-of-jquerys-append/ |
---|
| 156 | if($(node).find(prefixTheClassName("columnbreak", true)).length){ |
---|
| 157 | // |
---|
| 158 | // our column is on a column break, so just end here |
---|
| 159 | return; |
---|
| 160 | } |
---|
| 161 | if($(node).hasClass(prefixTheClassName("columnbreak"))){ |
---|
| 162 | // |
---|
| 163 | // our column is on a column break, so just end here |
---|
| 164 | return; |
---|
| 165 | } |
---|
| 166 | appendSafe($putInHere, $(node)); |
---|
| 167 | } |
---|
| 168 | if($putInHere[0].childNodes.length === 0) return; |
---|
| 169 | |
---|
| 170 | // now we're too tall, so undo the last one |
---|
| 171 | var kids = $putInHere[0].childNodes; |
---|
| 172 | var lastKid = kids[kids.length-1]; |
---|
| 173 | $putInHere[0].removeChild(lastKid); |
---|
| 174 | var $item = $(lastKid); |
---|
| 175 | |
---|
| 176 | // now lets try to split that last node |
---|
| 177 | // to fit as much of it as we can into this column |
---|
| 178 | if($item[0].nodeType == 3){ |
---|
| 179 | // it's a text node, split it up |
---|
| 180 | var oText = $item[0].nodeValue; |
---|
| 181 | var counter2 = options.width / 18; |
---|
| 182 | if(options.accuracy) |
---|
| 183 | counter2 = options.accuracy; |
---|
| 184 | var columnText; |
---|
| 185 | var latestTextNode = null; |
---|
| 186 | while($parentColumn.height() < targetHeight && oText.length){ |
---|
| 187 | // |
---|
| 188 | // it's been brought up that this won't work for chinese |
---|
| 189 | // or other languages that don't have the same use of whitespace |
---|
| 190 | // as english. This will need to be updated in the future |
---|
| 191 | // to better handle non-english languages. |
---|
| 192 | // |
---|
| 193 | // https://github.com/adamwulf/Columnizer-jQuery-Plugin/issues/124 |
---|
| 194 | var indexOfSpace = oText.indexOf(' ', counter2); |
---|
| 195 | if (indexOfSpace != -1) { |
---|
| 196 | columnText = oText.substring(0, indexOfSpace); |
---|
| 197 | } else { |
---|
| 198 | columnText = oText; |
---|
| 199 | } |
---|
| 200 | latestTextNode = document.createTextNode(columnText); |
---|
| 201 | appendSafe($putInHere, $(latestTextNode)); |
---|
| 202 | |
---|
| 203 | if(oText.length > counter2 && indexOfSpace != -1){ |
---|
| 204 | oText = oText.substring(indexOfSpace); |
---|
| 205 | }else{ |
---|
| 206 | oText = ""; |
---|
| 207 | } |
---|
| 208 | } |
---|
| 209 | if($parentColumn.height() >= targetHeight && latestTextNode !== null){ |
---|
| 210 | // too tall :( |
---|
| 211 | $putInHere[0].removeChild(latestTextNode); |
---|
| 212 | oText = latestTextNode.nodeValue + oText; |
---|
| 213 | } |
---|
| 214 | if(oText.length){ |
---|
| 215 | $item[0].nodeValue = oText; |
---|
| 216 | }else{ |
---|
| 217 | return false; // we ate the whole text node, move on to the next node |
---|
| 218 | } |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | if($pullOutHere.contents().length){ |
---|
| 222 | $pullOutHere.prepend($item); |
---|
| 223 | }else{ |
---|
| 224 | appendSafe($pullOutHere, $item); |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | return $item[0].nodeType == 3; |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | /** |
---|
| 231 | * Split up an element, which is more complex than splitting text. We need to create |
---|
| 232 | * two copies of the element with it's contents divided between each |
---|
| 233 | */ |
---|
| 234 | function split($putInHere, $pullOutHere, $parentColumn, targetHeight){ |
---|
| 235 | if($putInHere.contents(":last").find(prefixTheClassName("columnbreak", true)).length){ |
---|
| 236 | // |
---|
| 237 | // our column is on a column break, so just end here |
---|
| 238 | return; |
---|
| 239 | } |
---|
| 240 | if($putInHere.contents(":last").hasClass(prefixTheClassName("columnbreak"))){ |
---|
| 241 | // |
---|
| 242 | // our column is on a column break, so just end here |
---|
| 243 | return; |
---|
| 244 | } |
---|
| 245 | if($pullOutHere.contents().length){ |
---|
| 246 | var $cloneMe = $pullOutHere.contents(":first"); |
---|
| 247 | // |
---|
| 248 | // make sure we're splitting an element |
---|
| 249 | if( typeof $cloneMe.get(0) == 'undefined' || $cloneMe.get(0).nodeType != 1 ) return; |
---|
| 250 | |
---|
| 251 | // |
---|
| 252 | // clone the node with all data and events |
---|
| 253 | var $clone = $cloneMe.clone(true); |
---|
| 254 | // |
---|
| 255 | // need to support both .prop and .attr if .prop doesn't exist. |
---|
| 256 | // this is for backwards compatibility with older versions of jquery. |
---|
| 257 | if($cloneMe.hasClass(prefixTheClassName("columnbreak"))){ |
---|
| 258 | // |
---|
| 259 | // ok, we have a columnbreak, so add it into |
---|
| 260 | // the column and exit |
---|
| 261 | appendSafe($putInHere, $clone); |
---|
| 262 | $cloneMe.remove(); |
---|
| 263 | }else if (manualBreaks){ |
---|
| 264 | // keep adding until we hit a manual break |
---|
| 265 | appendSafe($putInHere, $clone); |
---|
| 266 | $cloneMe.remove(); |
---|
| 267 | }else if($clone.get(0).nodeType == 1 && !$clone.hasClass(prefixTheClassName("dontend"))){ |
---|
| 268 | appendSafe($putInHere, $clone); |
---|
| 269 | if($clone.is("img") && $parentColumn.height() < targetHeight + 20){ |
---|
| 270 | // |
---|
| 271 | // we can't split an img in half, so just add it |
---|
| 272 | // to the column and remove it from the pullOutHere section |
---|
| 273 | $cloneMe.remove(); |
---|
| 274 | }else if($cloneMe.hasClass(prefixTheClassName("dontsplit")) && $parentColumn.height() < targetHeight + 20){ |
---|
| 275 | // |
---|
| 276 | // pretty close fit, and we're not allowed to split it, so just |
---|
| 277 | // add it to the column, remove from pullOutHere, and be done |
---|
| 278 | $cloneMe.remove(); |
---|
| 279 | }else if($clone.is("img") || $cloneMe.hasClass(prefixTheClassName("dontsplit"))){ |
---|
| 280 | // |
---|
| 281 | // it's either an image that's too tall, or an unsplittable node |
---|
| 282 | // that's too tall. leave it in the pullOutHere and we'll add it to the |
---|
| 283 | // next column |
---|
| 284 | $clone.remove(); |
---|
| 285 | }else{ |
---|
| 286 | // |
---|
| 287 | // ok, we're allowed to split the node in half, so empty out |
---|
| 288 | // the node in the column we're building, and start splitting |
---|
| 289 | // it in half, leaving some of it in pullOutHere |
---|
| 290 | $clone.empty(); |
---|
| 291 | if(!columnize($clone, $cloneMe, $parentColumn, targetHeight)){ |
---|
| 292 | // this node may still have non-text nodes to split |
---|
| 293 | // add the split class and then recur |
---|
| 294 | $cloneMe.addClass(prefixTheClassName("split")); |
---|
| 295 | |
---|
| 296 | //if this node was ol element, the child should continue the number ordering |
---|
| 297 | if($cloneMe.get(0).tagName == 'OL'){ |
---|
| 298 | var startWith = $clone.get(0).childElementCount + $clone.get(0).start; |
---|
| 299 | $cloneMe.attr('start',startWith+1); |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | if($cloneMe.children().length){ |
---|
| 303 | split($clone, $cloneMe, $parentColumn, targetHeight); |
---|
| 304 | } |
---|
| 305 | }else{ |
---|
| 306 | // this node only has text node children left, add the |
---|
| 307 | // split class and move on. |
---|
| 308 | $cloneMe.addClass(prefixTheClassName("split")); |
---|
| 309 | } |
---|
| 310 | if($clone.get(0).childNodes.length === 0){ |
---|
| 311 | // it was split, but nothing is in it :( |
---|
| 312 | $clone.remove(); |
---|
| 313 | $cloneMe.removeClass(prefixTheClassName("split")); |
---|
| 314 | } |
---|
| 315 | } |
---|
| 316 | } |
---|
| 317 | } |
---|
| 318 | } |
---|
| 319 | |
---|
| 320 | |
---|
| 321 | function singleColumnizeIt() { |
---|
| 322 | if ($inBox.data("columnized") && $inBox.children().length == 1) { |
---|
| 323 | return; |
---|
| 324 | } |
---|
| 325 | $inBox.data("columnized", true); |
---|
| 326 | $inBox.data("columnizing", true); |
---|
| 327 | |
---|
| 328 | $inBox.empty(); |
---|
| 329 | $inBox.append($("<div class='" |
---|
| 330 | + prefixTheClassName("first") + " " |
---|
| 331 | + prefixTheClassName("last") + " " |
---|
| 332 | + prefixTheClassName("column") + " " |
---|
| 333 | + "' style='width:100%; float: " + options.columnFloat + ";'></div>")); //" |
---|
| 334 | $col = $inBox.children().eq($inBox.children().length-1); |
---|
| 335 | $destroyable = $cache.clone(true); |
---|
| 336 | if(options.overflow){ |
---|
| 337 | targetHeight = options.overflow.height; |
---|
| 338 | columnize($col, $destroyable, $col, targetHeight); |
---|
| 339 | // make sure that the last item in the column isn't a "dontend" |
---|
| 340 | if(!$destroyable.contents().find(":first-child").hasClass(prefixTheClassName("dontend"))){ |
---|
| 341 | split($col, $destroyable, $col, targetHeight); |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | while($col.contents(":last").length && checkDontEndColumn($col.contents(":last").get(0))){ |
---|
| 345 | var $lastKid = $col.contents(":last"); |
---|
| 346 | $lastKid.remove(); |
---|
| 347 | $destroyable.prepend($lastKid); |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | var html = ""; |
---|
| 351 | var div = document.createElement('DIV'); |
---|
| 352 | while($destroyable[0].childNodes.length > 0){ |
---|
| 353 | var kid = $destroyable[0].childNodes[0]; |
---|
| 354 | if(kid.attributes){ |
---|
| 355 | for(var i=0;i<kid.attributes.length;i++){ |
---|
| 356 | if(kid.attributes[i].nodeName.indexOf("jQuery") === 0){ |
---|
| 357 | kid.removeAttribute(kid.attributes[i].nodeName); |
---|
| 358 | } |
---|
| 359 | } |
---|
| 360 | } |
---|
| 361 | div.innerHTML = ""; |
---|
| 362 | div.appendChild($destroyable[0].childNodes[0]); |
---|
| 363 | html += div.innerHTML; |
---|
| 364 | } |
---|
| 365 | var overflow = $(options.overflow.id)[0]; |
---|
| 366 | overflow.innerHTML = html; |
---|
| 367 | |
---|
| 368 | }else{ |
---|
| 369 | appendSafe($col, $destroyable.contents()); |
---|
| 370 | } |
---|
| 371 | $inBox.data("columnizing", false); |
---|
| 372 | |
---|
| 373 | if(options.overflow && options.overflow.doneFunc){ |
---|
| 374 | options.overflow.doneFunc(); |
---|
| 375 | } |
---|
| 376 | |
---|
| 377 | } |
---|
| 378 | |
---|
| 379 | /** |
---|
| 380 | * returns true if the input dom node |
---|
| 381 | * should not end a column. |
---|
| 382 | * returns false otherwise |
---|
| 383 | */ |
---|
| 384 | function checkDontEndColumn(dom){ |
---|
| 385 | if(dom.nodeType == 3){ |
---|
| 386 | // text node. ensure that the text |
---|
| 387 | // is not 100% whitespace |
---|
| 388 | if(/^\s+$/.test(dom.nodeValue)){ |
---|
| 389 | // |
---|
| 390 | // ok, it's 100% whitespace, |
---|
| 391 | // so we should return checkDontEndColumn |
---|
| 392 | // of the inputs previousSibling |
---|
| 393 | if(!dom.previousSibling) return false; |
---|
| 394 | return checkDontEndColumn(dom.previousSibling); |
---|
| 395 | } |
---|
| 396 | return false; |
---|
| 397 | } |
---|
| 398 | if(dom.nodeType != 1) return false; |
---|
| 399 | if($(dom).hasClass(prefixTheClassName("dontend"))) return true; |
---|
| 400 | if(dom.childNodes.length === 0) return false; |
---|
| 401 | return checkDontEndColumn(dom.childNodes[dom.childNodes.length-1]); |
---|
| 402 | } |
---|
| 403 | |
---|
| 404 | function columnizeIt() { |
---|
| 405 | //reset adjustment var |
---|
| 406 | adjustment = 0; |
---|
| 407 | if(lastWidth == $inBox.width()) return; |
---|
| 408 | lastWidth = $inBox.width(); |
---|
| 409 | |
---|
| 410 | var numCols = Math.round($inBox.width() / options.width); |
---|
| 411 | var optionWidth = options.width; |
---|
| 412 | var optionHeight = options.height; |
---|
| 413 | if(options.columns) numCols = options.columns; |
---|
| 414 | if(manualBreaks){ |
---|
| 415 | numCols = $cache.find(prefixTheClassName("columnbreak", true)).length + 1; |
---|
| 416 | optionWidth = false; |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | // if ($inBox.data("columnized") && numCols == $inBox.children().length) { |
---|
| 420 | // return; |
---|
| 421 | // } |
---|
| 422 | if(numCols <= 1){ |
---|
| 423 | return singleColumnizeIt(); |
---|
| 424 | } |
---|
| 425 | if($inBox.data("columnizing")) return; |
---|
| 426 | $inBox.data("columnized", true); |
---|
| 427 | $inBox.data("columnizing", true); |
---|
| 428 | |
---|
| 429 | $inBox.empty(); |
---|
| 430 | $inBox.append($("<div style='width:" + (Math.floor(100 / numCols))+ "%; float: " + options.columnFloat + ";'></div>")); //" |
---|
| 431 | $col = $inBox.children(":last"); |
---|
| 432 | appendSafe($col, $cache.clone()); |
---|
| 433 | maxHeight = $col.height(); |
---|
| 434 | $inBox.empty(); |
---|
| 435 | |
---|
| 436 | var targetHeight = maxHeight / numCols; |
---|
| 437 | var firstTime = true; |
---|
| 438 | var maxLoops = 3; |
---|
| 439 | var scrollHorizontally = false; |
---|
| 440 | if(options.overflow){ |
---|
| 441 | maxLoops = 1; |
---|
| 442 | targetHeight = options.overflow.height; |
---|
| 443 | }else if(optionHeight && optionWidth){ |
---|
| 444 | maxLoops = 1; |
---|
| 445 | targetHeight = optionHeight; |
---|
| 446 | scrollHorizontally = true; |
---|
| 447 | } |
---|
| 448 | |
---|
| 449 | // |
---|
| 450 | // We loop as we try and workout a good height to use. We know it initially as an average |
---|
| 451 | // but if the last column is higher than the first ones (which can happen, depending on split |
---|
| 452 | // points) we need to raise 'adjustment'. We try this over a few iterations until we're 'solid'. |
---|
| 453 | // |
---|
| 454 | // also, lets hard code the max loops to 20. that's /a lot/ of loops for columnizer, |
---|
| 455 | // and should keep run aways in check. if somehow someone has content combined with |
---|
| 456 | // options that would cause an infinite loop, then this'll definitely stop it. |
---|
| 457 | for(var loopCount=0;loopCount<maxLoops && loopCount<20;loopCount++){ |
---|
| 458 | $inBox.empty(); |
---|
| 459 | var $destroyable, className, $col, $lastKid; |
---|
| 460 | try{ |
---|
| 461 | $destroyable = $cache.clone(true); |
---|
| 462 | }catch(e){ |
---|
| 463 | // jquery in ie6 can't clone with true |
---|
| 464 | $destroyable = $cache.clone(); |
---|
| 465 | } |
---|
| 466 | $destroyable.css("visibility", "hidden"); |
---|
| 467 | // create the columns |
---|
| 468 | for (var i = 0; i < numCols; i++) { |
---|
| 469 | /* create column */ |
---|
| 470 | className = (i === 0) ? prefixTheClassName("first") : ""; |
---|
| 471 | className += " " + prefixTheClassName("column"); |
---|
| 472 | className = (i == numCols - 1) ? (prefixTheClassName("last") + " " + className) : className; |
---|
| 473 | $inBox.append($("<div class='" + className + "' style='width:" + (Math.floor(100 / numCols))+ "%; float: " + options.columnFloat + ";'></div>")); //" |
---|
| 474 | } |
---|
| 475 | |
---|
| 476 | // fill all but the last column (unless overflowing) |
---|
| 477 | i = 0; |
---|
| 478 | while(i < numCols - (options.overflow ? 0 : 1) || scrollHorizontally && $destroyable.contents().length){ |
---|
| 479 | if($inBox.children().length <= i){ |
---|
| 480 | // we ran out of columns, make another |
---|
| 481 | $inBox.append($("<div class='" + className + "' style='width:" + (Math.floor(100 / numCols))+ "%; float: " + options.columnFloat + ";'></div>")); //" |
---|
| 482 | } |
---|
| 483 | $col = $inBox.children().eq(i); |
---|
| 484 | if(scrollHorizontally){ |
---|
| 485 | $col.width(optionWidth + "px"); |
---|
| 486 | } |
---|
| 487 | columnize($col, $destroyable, $col, targetHeight); |
---|
| 488 | // make sure that the last item in the column isn't a "dontend" |
---|
| 489 | split($col, $destroyable, $col, targetHeight); |
---|
| 490 | |
---|
| 491 | while($col.contents(":last").length && checkDontEndColumn($col.contents(":last").get(0))){ |
---|
| 492 | $lastKid = $col.contents(":last"); |
---|
| 493 | $lastKid.remove(); |
---|
| 494 | $destroyable.prepend($lastKid); |
---|
| 495 | } |
---|
| 496 | i++; |
---|
| 497 | |
---|
| 498 | // |
---|
| 499 | // https://github.com/adamwulf/Columnizer-jQuery-Plugin/issues/47 |
---|
| 500 | // |
---|
| 501 | // check for infinite loop. |
---|
| 502 | // |
---|
| 503 | // this could happen when a dontsplit or dontend item is taller than the column |
---|
| 504 | // we're trying to build, and its never actually added to a column. |
---|
| 505 | // |
---|
| 506 | // this results in empty columns being added with the dontsplit item |
---|
| 507 | // perpetually waiting to get put into a column. lets force the issue here |
---|
| 508 | if($col.contents().length === 0 && $destroyable.contents().length){ |
---|
| 509 | // |
---|
| 510 | // ok, we're building zero content columns. this'll happen forever |
---|
| 511 | // since nothing can ever get taken out of destroyable. |
---|
| 512 | // |
---|
| 513 | // to fix, lets put 1 item from destroyable into the empty column |
---|
| 514 | // before we iterate |
---|
| 515 | $col.append($destroyable.contents(":first")); |
---|
| 516 | }else if(i == numCols - (options.overflow ? 0 : 1) && !options.overflow){ |
---|
| 517 | // |
---|
| 518 | // ok, we're about to exit the while loop because we're done with all |
---|
| 519 | // columns except the last column. |
---|
| 520 | // |
---|
| 521 | // if $destroyable still has columnbreak nodes in it, then we need to keep |
---|
| 522 | // looping and creating more columns. |
---|
| 523 | if($destroyable.find(prefixTheClassName("columnbreak", true)).length){ |
---|
| 524 | numCols ++; |
---|
| 525 | } |
---|
| 526 | } |
---|
| 527 | } |
---|
| 528 | if(options.overflow && !scrollHorizontally){ |
---|
| 529 | var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/; |
---|
| 530 | var IE7 = (document.all) && (navigator.appVersion.indexOf("MSIE 7.") != -1); |
---|
| 531 | if(IE6 || IE7){ |
---|
| 532 | var html = ""; |
---|
| 533 | var div = document.createElement('DIV'); |
---|
| 534 | while($destroyable[0].childNodes.length > 0){ |
---|
| 535 | var kid = $destroyable[0].childNodes[0]; |
---|
| 536 | for(i=0;i<kid.attributes.length;i++){ |
---|
| 537 | if(kid.attributes[i].nodeName.indexOf("jQuery") === 0){ |
---|
| 538 | kid.removeAttribute(kid.attributes[i].nodeName); |
---|
| 539 | } |
---|
| 540 | } |
---|
| 541 | div.innerHTML = ""; |
---|
| 542 | div.appendChild($destroyable[0].childNodes[0]); |
---|
| 543 | html += div.innerHTML; |
---|
| 544 | } |
---|
| 545 | var overflow = $(options.overflow.id)[0]; |
---|
| 546 | overflow.innerHTML = html; |
---|
| 547 | }else{ |
---|
| 548 | $(options.overflow.id).empty().append($destroyable.contents().clone(true)); |
---|
| 549 | } |
---|
| 550 | }else if(!scrollHorizontally){ |
---|
| 551 | // the last column in the series |
---|
| 552 | $col = $inBox.children().eq($inBox.children().length-1); |
---|
| 553 | $destroyable.contents().each( function() { |
---|
| 554 | $col.append( $(this) ); |
---|
| 555 | }); |
---|
| 556 | var afterH = $col.height(); |
---|
| 557 | var diff = afterH - targetHeight; |
---|
| 558 | var totalH = 0; |
---|
| 559 | var min = 10000000; |
---|
| 560 | var max = 0; |
---|
| 561 | var lastIsMax = false; |
---|
| 562 | var numberOfColumnsThatDontEndInAColumnBreak = 0; |
---|
| 563 | $inBox.children().each(function($inBox){ return function($item){ |
---|
| 564 | var $col = $inBox.children().eq($item); |
---|
| 565 | var endsInBreak = $col.children(":last").find(prefixTheClassName("columnbreak", true)).length; |
---|
| 566 | if(!endsInBreak){ |
---|
| 567 | var h = $col.height(); |
---|
| 568 | lastIsMax = false; |
---|
| 569 | totalH += h; |
---|
| 570 | if(h > max) { |
---|
| 571 | max = h; |
---|
| 572 | lastIsMax = true; |
---|
| 573 | } |
---|
| 574 | if(h < min) min = h; |
---|
| 575 | numberOfColumnsThatDontEndInAColumnBreak++; |
---|
| 576 | } |
---|
| 577 | }; |
---|
| 578 | }($inBox)); |
---|
| 579 | |
---|
| 580 | var avgH = totalH / numberOfColumnsThatDontEndInAColumnBreak; |
---|
| 581 | if(totalH === 0){ |
---|
| 582 | // |
---|
| 583 | // all columns end in a column break, |
---|
| 584 | // so we're done here |
---|
| 585 | loopCount = maxLoops; |
---|
| 586 | }else if(options.lastNeverTallest && lastIsMax){ |
---|
| 587 | // the last column is the tallest |
---|
| 588 | // so allow columns to be taller |
---|
| 589 | // and retry |
---|
| 590 | // |
---|
| 591 | // hopefully this'll mean more content fits into |
---|
| 592 | // earlier columns, so that the last column |
---|
| 593 | // can be shorter than the rest |
---|
| 594 | adjustment += 5; |
---|
| 595 | |
---|
| 596 | targetHeight = targetHeight + 30; |
---|
| 597 | if(loopCount == maxLoops-1) maxLoops++; |
---|
| 598 | }else if(max - min > 30){ |
---|
| 599 | // too much variation, try again |
---|
| 600 | targetHeight = avgH + 30; |
---|
| 601 | }else if(Math.abs(avgH-targetHeight) > 20){ |
---|
| 602 | // too much variation, try again |
---|
| 603 | targetHeight = avgH; |
---|
| 604 | }else { |
---|
| 605 | // solid, we're done |
---|
| 606 | loopCount = maxLoops; |
---|
| 607 | } |
---|
| 608 | }else{ |
---|
| 609 | // it's scrolling horizontally, fix the width/classes of the columns |
---|
| 610 | $inBox.children().each(function(i){ |
---|
| 611 | $col = $inBox.children().eq(i); |
---|
| 612 | $col.width(optionWidth + "px"); |
---|
| 613 | if(i === 0){ |
---|
| 614 | $col.addClass(prefixTheClassName("first")); |
---|
| 615 | }else if(i==$inBox.children().length-1){ |
---|
| 616 | $col.addClass(prefixTheClassName("last")); |
---|
| 617 | }else{ |
---|
| 618 | $col.removeClass(prefixTheClassName("first")); |
---|
| 619 | $col.removeClass(prefixTheClassName("last")); |
---|
| 620 | } |
---|
| 621 | }); |
---|
| 622 | $inBox.width($inBox.children().length * optionWidth + "px"); |
---|
| 623 | } |
---|
| 624 | $inBox.append($("<br style='clear:both;'>")); |
---|
| 625 | } |
---|
| 626 | $inBox.find(prefixTheClassName("column", true)).find(":first" + prefixTheClassName("removeiffirst", true)).remove(); |
---|
| 627 | $inBox.find(prefixTheClassName("column", true)).find(':last' + prefixTheClassName("removeiflast", true)).remove(); |
---|
| 628 | $inBox.find(prefixTheClassName("split", true)).find(":first" + prefixTheClassName("removeiffirst", true)).remove(); |
---|
| 629 | $inBox.find(prefixTheClassName("split", true)).find(':last' + prefixTheClassName("removeiflast", true)).remove(); |
---|
| 630 | $inBox.data("columnizing", false); |
---|
| 631 | |
---|
| 632 | if(options.overflow){ |
---|
| 633 | options.overflow.doneFunc(); |
---|
| 634 | } |
---|
| 635 | options.doneFunc(); |
---|
| 636 | } |
---|
| 637 | }); |
---|
| 638 | }; |
---|
| 639 | })(jQuery); |
---|