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.