PageSourceSearch

https://www.livingwage.org.uk/libraries/jquery-mosaic/jquery.mosaic.js?tm4dx4

js livingwage.org.uk collected 2026-10-01 09:02:58 UTC 11,816 bytes, 328 lines download raw bytes

1/*
2	 jQuery Mosaic v0.15.3
3	 https://github.com/tin-cat/jquery-mosaic
4	 A jquery plugin by Tin.cat to build beautifully arranged and responsive mosaics of html elements maintaining their original aspect ratio. Works wonderfully with images by creating a visually ordered and pleasant mosaic (much like mosaics on Flickr, 500px and Google+) without gaps between elements, but at the same time respecting aspect ratios. Reacts to window resizes and adapts responsively to any screen size. See it working on https://skinography.net
5 */
6
7(function($){
8
9	$.Mosaic = function(el, options) {
10		var base = this, o;
11			
12		base.el = el;
13		base.$el = $(el);
14
15		base.$el.data('Mosaic', base);
16
17		var baseWidth;
18		var refitTimeout = false;
19
20		base.init = function() {
21			// Priority of parameters : JS options > HTML data options > DEFAULT options
22			base.options = o = $.extend({}, $.Mosaic.defaults, base.$el.data(), options);
23			
24			$(base.el).addClass("jQueryMosaic");
25
26			if (o.outerMargin)
27				$(base.el).css('padding', o.outerMargin);
28
29			if (o.innerGap)
30				$(base.el).css('margin-bottom', o.innerGap * -1);
31
32			// If width and height are specified via attribute, set width and height as css properties to solve weird IE problem.
33			base.getItems().each(function(idx, item) {
34				if ($(item).attr('width'))
35					$(item).width($(item).attr('width'));
36				if ($(item).attr('height'))
37					$(item).height($(item).attr('height'));
38			});
39			
40			base.fit();
41
42			if (o.refitOnResize)
43				$(window).on('resize', null, null, function() {
44					if ($(base.el).is(':hidden'))
45						return;
46					if (o.refitOnResizeDelay) {
47						if (refitTimeout)
48							clearTimeout(refitTimeout);
49						refitTimeout = setTimeout(function() {
50							base.fit()
51						}, o.refitOnResizeDelay);
52					}
53					else
54						base.fit()
55				});
56		}
57
58		base.getItems = function() {
59			return $('> div:not([data-no-mosaic=true]), > a:not([data-no-mosaic=true]), > img:not([data-no-mosaic=true]), > video:not([data-no-mosaic=true])', base.el);
60		}
61
62		base.getItemAtIndex = function(index) {
63			if (!base.getItems()[index])
64				return false;
65			return $(base.getItems()[index]);
66		}
67
68		base.getItemsSubset = function(start, numberOf) {
69			var items = base.getItems();
70			if (!numberOf)
71				numberOf = items.length - start;
72			if (start > items.length)
73				return false;
74			if (start + numberOf > items.length)
75				numberOf = items.length - start;
76			return items.slice(start, start + numberOf);
77		}
78
79		base.isLastItemsSubset = function(start, numberOf) {
80			var items = base.getItems();
81			if (start > items.length)
82				return true;
83			if (start + numberOf > items.length)
84				return true;
85			return false;
86		}
87
88		base.getItemWidth = function(item) {
89			if ($(item).outerWidth())
90				return $(item).outerWidth();
91			if ($(item).attr('width'))
92				return $(item).attr('width');
93		}
94
95		base.getItemHeight = function(item) {
96			if ($(item).outerHeight())
97				return $(item).outerHeight();
98			if ($(item).attr('height'))
99				return $(item).attr('height');
100		}
101
102		base.getItemAspectRatio = function(item) {
103			if ($(item).data('aspect-ratio'))
104				return $(item).data('aspect-ratio');
105			if (base.getItemWidth(item) && base.getItemHeight(item)) {
106				var aspectRatio = base.getItemWidth(item) / base.getItemHeight(item);
107				$(item).data('aspect-ratio', aspectRatio);
108				return aspectRatio;
109			}
110			return o.defaultAspectRatio;
111		}
112
113		base.getItemWidthForGivenHeight = function(item, height) {
114			return height * base.getItemAspectRatio(item);
115		}
116
117		base.getItemHeightForGivenWidth = function(item, width) {
118			return width / base.getItemAspectRatio(item);
119		}
120
121		base.setItemSizeByGivenHeight = function(item, height, isDoNotSetHeight) {
122			var width = Math.floor(base.getItemWidthForGivenHeight(item, height));
123
124			$(item).width(width + 'px');
125			if (isDoNotSetHeight)
126				$(item).height('auto');
127			else
128				$(item).height(Math.floor(height));
129
130			base.handleHighResForItem(item);
131
132			return width;
133		}
134
135		base.handleHighResForItem = function(item) {
136			if (!o.highResImagesWidthThreshold)
137				return;
138
139			var width = $(item).width();
140
141			if (width > o.highResImagesWidthThreshold) {
142				// Set high res version images where needed
143
144				// Get all elements inside this item having a high-res-background-image-url data, including the item itself, if it has it also
145				var itemsWithHighResData = $('[data-high-res-background-image-url]', item);
146
147				if ($(item).data('high-res-background-image-url'))
148					itemsWithHighResData = $(itemsWithHighResData).add(item);
149
150				$(itemsWithHighResData).each(function() {
151					$(this).css('background-image', 'url("' + $(this).data('high-res-background-image-url') + '"');
152					$(this).addClass('highRes');
153				});
154
155
156				// Get all images inside this item having a high-res-image-url data, including the item itself, if it has it also
157				var itemsWithHighResData = $('[data-high-res-image-src]', item);
158
159				if ($(item).data('high-res-image-src'))
160					itemsWithHighResData = $(itemsWithHighResData).add(item);
161
162				$(itemsWithHighResData).each(function() {
163					$(this).attr('src', $(this).data('high-res-image-src'));
164					$(this).addClass('highRes');
165				});
166
167			}
168		}
169
170		base.calculateHeightToFit = function(items) {
171			var sumAspectRatios = 0;
172			items.each(function() {
173				sumAspectRatios += parseFloat(base.getItemAspectRatio(this));
174			});
175			return (baseWidth - (o.innerGap * (items.length - 1))) / sumAspectRatios;
176		}
177
178		// Based on the method by glen.codes to get subpixel attributes of elements: https://glen.codes/getting-the-subpixel-width-of-an-element/, to try and solve the bug in jQuery versions < 3: https://github.com/jquery/jquery/issues/1724
179		base.retrieveBaseWidth = function() {
180			var value = parseFloat($(base.el).width());
181			baseWidth = $.isNumeric(value) ? value : 0;
182		}
183
184		base.isBelowResponsiveWidthThreshold = function() {
185			return o.responsiveWidthThreshold && baseWidth < o.responsiveWidthThreshold;
186		}
187
188		base.fit = function() {
189			base.retrieveBaseWidth();
190
191			if (base.isBelowResponsiveWidthThreshold()) {
192
193				var items = base.getItems();
194
195				if (o.maxItemsToShowWhenResponsiveThresholdSurpassed) {
196					$(items).slice(o.maxItemsToShowWhenResponsiveThresholdSurpassed).remove();
197					items = $(items).slice(0, o.maxItemsToShowWhenResponsiveThresholdSurpassed);
198				}
199
200				items.each(function() {
201					var height = base.getItemHeightForGivenWidth(this, baseWidth);
202
203					$(this).width(baseWidth)
204
205					if ($(this).data('only-force-height-when-necessary'))
206						$(this).height('auto');
207					else
208						$(this).height(height);
209
210					if (o.innerGap)
211						$(this).css('margin-bottom', o.innerGap);
212				});
213
214				return;
215			}
216
217			var items, height;
218			var itemsToUse = 1;
219			var startIndex = 0;
220			var isAnyFitted = false;
221			var row = 1;
222			while (true) {
223
224				items = base.getItemsSubset(startIndex, itemsToUse);
225
226				if (base.isLastItemsSubset(startIndex, itemsToUse)) {
227					if (items.length) {
228						base.fitItems(items);
229					}
230					break;
231				}
232
233				height = base.calculateHeightToFit(items);
234
235				if (height > o.maxRowHeight) {
236					itemsToUse ++;
237					continue;
238				}
239
240				base.fitItems(items);
241				startIndex += itemsToUse;
242				itemsToUse = 1;
243				isAnyFitted = true;
244
245				if (!base.isBelowResponsiveWidthThreshold() && o.maxRows && row == o.maxRows) {
246					base.getItemsSubset(startIndex).each(function() { $(this).hide(); });
247					return;
248				}
249
250				row ++;
251			}
252
253			// If maxRowHeight has not been met at any point (might happen when specifying short maxRowHeights)
254			if (!isAnyFitted) {
255				if (o.showTailWhenNotEnoughItemsForEvenOneRow)
256					o.maxRowHeightPolicy = 'tail';
257				base.fitItems(base.getItems());
258			}
259		}
260
261		base.fitItems = function(items) {
262			var isDoNothing = false;
263			var height = base.calculateHeightToFit(items);
264			if (height > o.maxRowHeight) {
265				switch (o.maxRowHeightPolicy) {
266					case 'skip':
267						items.each(function() { $(this).hide(); });
268						return;
269						break;
270					case 'crop':
271						height = o.maxRowHeight;
272						break;
273					case 'oversize':
274						// Do nothing
275						var isDoNothing = true;
276						break;
277					case 'tail':
278						height = o.maxRowHeight;
279						var isTail = true;
280						break;
281				}
282			}
283			items.each(function() { $(this).show(); });
284			var accumulatedWidth = 0;
285			items.each(function(idx) {
286				accumulatedWidth += base.setItemSizeByGivenHeight(this, height, isDoNothing && $(this).data('only-force-height-when-necessary')); 
287				if (o.innerGap) {
288					$(this).css('margin-right', idx < items.length - 1 ? o.innerGap : 0);
289					$(this).css('margin-bottom', o.innerGap);
290				}
291			});
292			// Compensate the last element for accumulated floored decimal widths leaving a gap at the end
293			if (!isTail && accumulatedWidth != (baseWidth - ((items.length - 1) * o.innerGap))) {
294				difference = (baseWidth - ((items.length - 1) * o.innerGap)) - accumulatedWidth;
295				var width = items.last().width();
296				items.last().width(width + difference);
297			}
298		}
299
300		base.init();
301	}
302
303	$.Mosaic.defaults = {
304		maxRowHeight: 400, // The maximum desired height of rows
305		refitOnResize: true, // Whether to rebuild the mosaic when the window is resized or not
306		refitOnResizeDelay: false, // Milliseconds to wait after a resize event to refit the mosaic. Useful when creating huge mosaics that can take some CPU time on the user's browser. Leave it to false to refit the mosaic in realtime.
307		defaultAspectRatio: 1, // The aspect ratio to use when none has been specified, or can't be calculated
308		maxRowHeightPolicy: 'skip', // Sometimes some of the remaining items cannot be fitted on a row without surpassing the maxRowHeight. For those cases, choose one of the available settings for maxRowHeightPolicy: "skip": Does not renders the unfitting items. "crop": caps the desired height to the specified maxRowHeight, resulting in those items not keeping their aspect ratios. "oversize": Renders the items respecting their aspect ratio but surpassing the specified maxRowHeight
309		maxRows: false, // In some scenarios you might need fine control about the maximum number of rows of the mosaic. If specified, the mosaic won't have more than this number of rows. If responsiveWidthThreshold is specified, maxRows are not considered when the threshold has been reached.
310		highResImagesWidthThreshold: 350, // The item width on which to start using the the provided high resolution image instead of the normal one. High resolution images are specified via the "data-high-res-image-src" or "data-high-res-background-image-url" html element properties of each item.
311		outerMargin: 0, // A margin size in pixels for the outher edge of the whole mosaic
312		innerGap: 0, // A gap size in pixels to leave a space between elements
313		responsiveWidthThreshold: false, // The minimum width for which to keep building the mosaic. If specified, when the width is less than this, the mosaic building logic is not applied, and one item per row is always shown. This might help you avoid resulting item sizes that are too small and might break complex html/css inside them, specially when aiming for great responsive mosaics.
314		maxItemsToShowWhenResponsiveThresholdSurpassed: false, // If set (and also responsiveWidthThreshold is set), only this amount of items will be shown when the responsiveWidthThreshold is met
315		showTailWhenNotEnoughItemsForEvenOneRow: false, // If this is set to true, when there are not enough items to fill even a single row, they will be shown anyway even if they do not complete the row horizontally. If left to false, no mosaic will be shown in such occasions.
316	};
317
318	$.fn.Mosaic = function(options, params) {
319		return this.each(function(){
320			var me = $(this).data('Mosaic');
321			if ((typeof(options)).match('object|undefined'))
322				new $.Mosaic(this, options);
323			else
324				eval('me.'+options)(params);
325		});
326	}
327
328})(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.