PageSourceSearch

https://jmsn.com/wp-content/plugins/bb-plugin/js/libs/jquery.mosaicflow.js?ver=2.11.1

js jmsn.com collected 2026-10-09 23:24:11 UTC 10,535 bytes, 372 lines download raw bytes

1/**
2 * Mosaic Flow
3 *
4 * Pinterest like responsive image grid that doesnt suck
5 *
6 * @requires jQuery
7 * @author Artem Sapegin
8 * @copyright 2012 Artem Sapegin, http://sapegin.me
9 * @license MIT
10 */
11/* taken at commit https://github.com/sapegin/jquery.mosaicflow/commit/20cea9ae73bab6bfeda54434564aaf0f86bed9ac
12 * which includes fn.load()
13/*
14/*jshint browser:true, jquery:true, white:false, smarttabs:true */
15/*global jQuery:false, define:false*/
16(function(factory) {  // Try to register as an anonymous AMD module
17	if (typeof define === 'function' && define.amd) {
18		define(['jquery'], factory);
19	}
20	else {
21		factory(jQuery);
22	}
23}(function($) {
24	'use strict';
25	var cnt = 0;
26
27	$.fn.mosaicflow = function(options) {
28		var args = Array.prototype.slice.call(arguments, 0);
29
30		return this.each(function() {
31			var elm = $(this);
32			var data = elm.data('mosaicflow');
33
34			if (!data) {
35				options = $.extend({}, $.fn.mosaicflow.defaults, options, dataToOptions(elm));
36				data = new Mosaicflow(elm, options);
37				elm.data('mosaicflow', data);
38			}
39			else if (typeof options === 'string') {
40				data[options](args[1]);
41			}
42		});
43	};
44
45	$.fn.mosaicflow.defaults = {
46		itemSelector: '> *',
47		columnClass: 'mosaicflow__column',
48		minItemWidth: 240,
49		minColumns: 2,
50		itemHeightCalculation: 'auto',
51		threshold: 40,
52		fallbackHTML: false
53	};
54
55	function Mosaicflow(container, options) {
56		this.container = container;
57		this.options = options;
58
59		this.container.trigger('mosaicflow-start');
60		this.init();
61		this.container.trigger('mosaicflow-ready');
62	}
63
64	Mosaicflow.prototype = {
65		init: function() {
66			this.__uid = cnt++;
67			this.__uidItemCounter = 0;
68			this.items = this.container.find(this.options.itemSelector);
69			this.columns = $([]);
70			this.columnsHeights = [];
71			this.itemsHeights = {};
72			this.tempContainer = $('<' + ( this.options.fallbackHTML ? 'div' : 'li' ) + '>', {
73				css: { 'visibility': 'hidden',	'width': '100%' },
74				role: this.options.fallbackHTML ? 'listitem' : null,
75			});
76			this.workOnTemp = false;
77			this.autoCalculation = this.options.itemHeightCalculation === 'auto';
78
79			this.container.append(this.tempContainer);
80
81			var that = this;
82			this.items.each(function() {
83				var elm = $(this);
84				var id = elm.attr('id');
85				if (!id) {
86					// Generate an unique id
87					id = that.generateUniqueId();
88					elm.attr('id', id);
89				}
90			});
91
92			this.container.css('visibility', 'hidden');
93			if (this.autoCalculation) {
94				$(window).on('load', $.proxy(this.refill, this));
95			}
96			else {
97				this.refill();
98			}
99			$(window).resize($.proxy(this.refill, this));
100		},
101
102		refill: function() {
103			this.container.trigger('mosaicflow-fill');
104			this.numberOfColumns = Math.floor(this.container.width() / this.options.minItemWidth);
105			// Always keep min columns number
106			if (this.numberOfColumns < this.options.minColumns)
107				this.numberOfColumns = this.options.minColumns;
108
109			var needToRefill = this.ensureColumns();
110			if (needToRefill) {
111				this.fillColumns();
112
113				// Remove excess columns, only if there are visible columns remaining
114				if (this.columns.filter(':visible').length > 0) {
115					this.columns.filter(':hidden').remove();
116				}
117			}
118			this.container.css('visibility', 'visible');
119			this.container.trigger('mosaicflow-filled');
120		},
121
122		ensureColumns: function() {
123			var createdCnt = this.columns.filter(':visible').length;
124			var calculatedCnt = this.numberOfColumns;
125
126			this.workingContainer = createdCnt === 0 ? this.tempContainer : this.container;
127
128			if (calculatedCnt > createdCnt) {
129				var neededCnt = calculatedCnt - createdCnt;
130				for (var columnIdx = 0; columnIdx < neededCnt; columnIdx++) {
131					var column = $('<' + ( this.options.fallbackHTML ? 'div' : 'li' ) + '>', {
132						class: this.options.columnClass,
133						role: this.options.fallbackHTML ? 'listitem' : null
134					});
135
136					this.workingContainer.append(column);
137				}
138			}
139			else if (calculatedCnt < createdCnt) {
140				var lastColumn = createdCnt;
141				while (calculatedCnt <= lastColumn) {
142					// We can't remove columns here becase it will remove items to. So we hide it and will remove later.
143					this.columns.eq(lastColumn).hide();
144					lastColumn--;
145				}
146
147				var diff = createdCnt - calculatedCnt;
148				this.columnsHeights.splice(this.columnsHeights.length - diff, diff);
149			}
150
151			if (calculatedCnt !== createdCnt) {
152				this.columns = this.workingContainer.find('.' + this.options.columnClass);
153				this.columns.css('width', (100 / calculatedCnt) + '%');
154				return true;
155			}
156
157			return false;
158		},
159
160		fillColumns: function() {
161			var columnsCnt = this.numberOfColumns;
162			var itemsCnt = this.items.length;
163
164			for (var columnIdx = 0; columnIdx < columnsCnt; columnIdx++) {
165				var column = this.columns.eq(columnIdx);
166				this.columnsHeights[columnIdx] = 0;
167				for (var itemIdx = columnIdx; itemIdx < itemsCnt; itemIdx += columnsCnt) {
168					var item = this.items.eq(itemIdx);
169					var height = 0;
170					column.append(item);
171
172					if (this.autoCalculation) {
173						// Check height after being placed in its column
174						height = item.outerHeight();
175					}
176					else {
177						// Read img height attribute
178						height = parseInt(item.find('img').attr('height'), 10);
179					}
180
181					this.itemsHeights[item.attr('id')] = height;
182					this.columnsHeights[columnIdx] += height;
183				}
184			}
185
186			this.levelBottomEdge(this.itemsHeights, this.columnsHeights);
187
188			if (this.workingContainer === this.tempContainer) {
189				this.container.append(this.tempContainer.children());
190			}
191			this.container.trigger('mosaicflow-layout');
192		},
193
194		levelBottomEdge: function(itemsHeights, columnsHeights) {
195			while (true) {
196				var lowestColumn = $.inArray(Math.min.apply(null, columnsHeights), columnsHeights);
197				var highestColumn = $.inArray(Math.max.apply(null, columnsHeights), columnsHeights);
198				if (lowestColumn === highestColumn) return;
199
200				var lastInHighestColumn = this.columns.eq(highestColumn).children().last();
201				var lastInHighestColumnHeight = itemsHeights[lastInHighestColumn.attr('id')];
202				var lowestHeight = columnsHeights[lowestColumn];
203				var highestHeight = columnsHeights[highestColumn];
204				var newLowestHeight = lowestHeight + lastInHighestColumnHeight;
205
206				if (newLowestHeight >= highestHeight) return;
207
208				if (highestHeight - newLowestHeight < this.options.threshold) return;
209
210				this.columns.eq(lowestColumn).append(lastInHighestColumn);
211				columnsHeights[highestColumn] -= lastInHighestColumnHeight;
212				columnsHeights[lowestColumn] += lastInHighestColumnHeight;
213			}
214		},
215
216		add: function(elm) {
217			this.container.trigger('mosaicflow-item-add', [elm]);
218			var lowestColumn = $.inArray(Math.min.apply(null, this.columnsHeights), this.columnsHeights);
219			var height = 0;
220
221			if (this.autoCalculation) {
222
223				// Get height of elm
224				elm.css({
225					position: 'static',
226					visibility: 'hidden',
227					display: 'block'
228				}).appendTo(this.columns.eq(lowestColumn));
229
230				height = elm.outerHeight();
231
232				var inlineImages = elm.find('img');
233				if (inlineImages.length !== 0) {
234					inlineImages.each(function() {
235						var image = $(this);
236						var imageSizes = getImageSizes(image);
237						var actualHeight = (image.width() * imageSizes.height) / imageSizes.width;
238
239						height += actualHeight;
240					});
241				}
242
243				elm.detach().css({
244					position: 'static',
245					visibility: 'visible'
246				});
247			}
248			else {
249				height = parseInt(elm.find('img').attr('height'), 10);
250			}
251
252			if (!elm.attr('id')) {
253				// Generate a unique id
254				elm.attr('id', this.generateUniqueId());
255			}
256
257			// Update item collection.
258			// Item needs to be placed at the end of this.items to keep order of elements
259			var itemsArr = this.items.toArray();
260			itemsArr.push(elm);
261			this.items = $(itemsArr);
262
263			this.itemsHeights[elm.attr('id')] = height;
264			this.columnsHeights[lowestColumn] += height;
265			this.columns.eq(lowestColumn).append(elm);
266
267			this.levelBottomEdge(this.itemsHeights, this.columnsHeights);
268			this.container.trigger('mosaicflow-layout');
269			this.container.trigger('mosaicflow-item-added', [elm]);
270		},
271
272		remove: function(elm) {
273			this.container.trigger('mosaicflow-item-remove', [elm]);
274			var column = elm.parents('.' + this.options.columnClass);
275
276			// Update column height
277			this.columnsHeights[column.index() - 1] -= this.itemsHeights[elm.attr('id')];
278
279			elm.detach();
280
281			// Update item collection
282			this.items = this.items.not(elm);
283			this.levelBottomEdge(this.itemsHeights, this.columnsHeights);
284			this.container.trigger('mosaicflow-layout');
285			this.container.trigger('mosaicflow-item-removed', [elm]);
286		},
287
288		empty: function() {
289			var columnsCnt = this.numberOfColumns;
290
291			this.items = $([]);
292			this.itemsHeights = {};
293
294			for (var columnIdx = 0; columnIdx < columnsCnt; columnIdx++) {
295				var column = this.columns.eq(columnIdx);
296				this.columnsHeights[columnIdx] = 0;
297				column.empty();
298			}
299			this.container.trigger('mosaicflow-layout');
300		},
301
302		recomputeHeights: function() {
303			function computeHeight(idx, item) {
304				item = $(item);
305				var height = 0;
306				if (that.autoCalculation) {
307					// Check height after being placed in its column
308					height = item.outerHeight();
309				}
310				else {
311					// Read img height attribute
312					height = parseInt(item.find('img').attr('height'), 10);
313				}
314
315				that.itemsHeights[item.attr('id')] = height;
316				that.columnsHeights[columnIdx] += height;
317			}
318
319			var that = this;
320			var columnsCnt = this.numberOfColumns;
321
322			for (var columnIdx = 0; columnIdx < columnsCnt; columnIdx++) {
323				var column = this.columns.eq(columnIdx);
324
325				this.columnsHeights[columnIdx] = 0;
326				column.children().each(computeHeight);
327			}
328		},
329
330		generateUniqueId: function() {
331			// Increment the counter
332			this.__uidItemCounter++;
333
334			// Return an unique ID
335			return 'mosaic-' + this.__uid + '-itemid-' + this.__uidItemCounter;
336		}
337	};
338
339	// Camelize data-attributes
340	function dataToOptions(elem) {
341		function upper(m, l) {
342			return l.toUpper();
343		}
344		var options = {};
345		var data = elem.data();
346		for (var key in data) {
347			options[key.replace(/-(\w)/g, upper)] = data[key];
348		}
349		return options;
350	}
351
352	function getImageSizes(image) {
353		var sizes = {};
354
355		sizes.height = parseInt(image.attr('height'), 10);
356		sizes.width = parseInt(image.attr('width'), 10);
357
358		if (sizes.height === 0 || sizes.width === 0) {
359			var utilImage = new Image();
360			utilImage.src = image.attr('src');
361
362			sizes.width = utilImage.width;
363			sizes.height = utilImage.height;
364		}
365
366		return sizes;
367	}
368
369	// Auto init
370	$(function() { $('.mosaicflow').mosaicflow(); });
371
372}));

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.