PageSourceSearch

https://www.lannoopublishers.com/sites/all/themes/custom/lea_theme…/columnizer/src/jquery.columnizer.js?tm4xxp

js lannoopublishers.com collected 2026-10-03 21:20:19 UTC 21,279 bytes, 608 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.