1/*! 2 * PerfectMasonry extension for Isotope 3 * 4 * Does similar things as the Isotopes "masonry" layoutmode, except that this one will actually go back and plug the holes 5 * left by bigger elements, thus making a perfect brick wall. Profit! 6 * 7 * Usage: 8 * $('#grid').isotope({ 9 * layoutMode: 'perfectMasonry', 10 * perfectMasonry: { 11 * layout: 'horizontal', // Set layout as vertical/horizontal (default: vertical) 12 * columnWidth: 200, // Set/Prefer columns to x wide (default: width of first tile) 13 * rowHeight: 200, // Set/Prefer rows to y high (default: height of first tile) 14 * 15 * liquid: true, // Set layout as liquid (default: false) 16 * cols: 3, // Force to have x columns (default: null) 17 * rows: 3, // Force to have y rows (default: null) 18 * minCols: 3, // Set min col count (default: 1) 19 * minRows: 3, // Set min row count (default: 1) 20 * maxCols: 5, // Set max col count (default: 9999) 21 * maxRows: 4 // Set max row count (default: 9999) 22 * } 23 * }); 24 * 25 * 26 * @author Mikko Tikkanen, Zonear Ltd. <[email protected]> 27 */ 28;(function($, undefined) { 29 var version = '1.2.1'; 30 var isotope = null, 31 $context = null, 32 $container = null; 33 34 $.extend($.Isotope.prototype, { 35 isFirstRun: true, 36 37 /** 38 * Reset layout properties 39 * 40 * Runs before any layout change 41 * -------------------------------------------------------------------------------------------------------- */ 42 _perfectMasonryReset: function() { 43 this.options.perfectMasonry = this.options.perfectMasonry|| {}; 44 var isVertical = this.options.perfectMasonry.layout != 'horizontal', 45 isLiquid = this.options.perfectMasonry.liquid == true; 46 47 // Do things on a first run 48 if(this.isFirstRun) { 49 this.isFirstRun = false; 50 isotope = this; 51 $context = $(this.element.context); 52 $container = $context.parent(); 53 54 // Make sure we have min/maxCols & rows 55 this.options.perfectMasonry.minCols = this.options.perfectMasonry.minCols || 1; 56 this.options.perfectMasonry.minRows = this.options.perfectMasonry.minRows || 1; 57 this.options.perfectMasonry.maxCols = this.options.perfectMasonry.maxCols || 9999; 58 this.options.perfectMasonry.maxRows = this.options.perfectMasonry.maxRows || 9999; 59 } 60 61 // For liquid layout, replace default resize handler with forced relayout (outside firstRun as Isotope grid can be destroyed and created on the fly) 62 if(isLiquid && $.data(window, 'events') && $._data(window, 'events').smartresize) { 63 $(window).off('smartresize.isotope'); 64 $(window).on('resize.isotope.perfectmasonry', function() { 65 if(!$context.hasClass('isotope')) { return; } 66 $context.isotope('reLayout'); 67 }); 68 } 69 70 // Setup layout properties --------------------------------------------------- 71 var properties = this.perfectMasonry = {}; 72 73 // Fill properties with columnWidth and rowHeight (true argument) 74 this._getSegments(); 75 this._getSegments(true); 76 77 // ...and with cols & rows 78 this._perfectMasonryGetSegments(); 79 80 // Handle liquid layout (cols, rows & sizes must be calculated on the fly) 81 if(isLiquid) { 82 var width = $container.width(), 83 height = $container.height(); 84 85 // Make sure we have colwidth & rowheight (get it from the calculated ) 86 this.options.perfectMasonry.columnWidth = this.options.perfectMasonry.columnWidth || properties.columnWidth; 87 this.options.perfectMasonry.rowHeight = this.options.perfectMasonry.rowHeight || properties.rowHeight; 88 89 // Figure out how many cols & rows either have been set or can be fit into the container (also make sure we're still between min/max) 90 properties.cols = this.options.perfectMasonry.cols || Math.floor(width / this.options.perfectMasonry.columnWidth); 91 properties.rows = this.options.perfectMasonry.rows || Math.floor(height / this.options.perfectMasonry.rowHeight); 92 properties.cols = Math.min(Math.max(properties.cols, this.options.perfectMasonry.minCols), this.options.perfectMasonry.maxCols); 93 properties.rows = Math.min(Math.max(properties.rows, this.options.perfectMasonry.minRows), this.options.perfectMasonry.maxRows); 94 95 // Recalculate accurate width/height so that the whole available space is used 96 var diff = (isVertical ? properties.columnWidth / (width / properties.cols) : properties.rowHeight / (height / properties.rows)); 97 properties.columnWidth = Math.floor(properties.columnWidth / diff); 98 properties.rowHeight = Math.floor(properties.rowHeight / diff); 99 } 100 101 // Create top row of the grid 102 properties.grid = new Array(this.perfectMasonry.cols); 103 104 // Set container dimensions to 0 105 properties.containerHeight = 0; 106 properties.containerWidth = 0; 107 }, 108 109 110 111 /** 112 * Create layout 113 * -------------------------------------------------------------------------------------------------------- */ 114 _perfectMasonryLayout: function($elems) { 115 var instance = this, 116 properties = this.perfectMasonry, 117 isVertical = instance.options.perfectMasonry.layout != 'horizontal', 118 isLiquid = instance.options.perfectMasonry.liquid == true; 119 120 // Create first set of the grid 121 properties.grid = new Array(properties[(isVertical ? 'cols' : 'rows')]);
122 if(!properties.grid || !properties.grid.length) { return; } 123 124 // Loop each element 125 $elems.each(function() { 126 var $this = $(this); 127 128 // Calculate col & row spans (with liquid layouts, store desired width as element data) 129 var colSpan = (isLiquid ? $this.data('colSpan') : Math.ceil($this.outerWidth() / (properties.columnWidth + 1))), 130 rowSpan = (isLiquid ? $this.data('rowSpan') : Math.ceil($this.outerHeight() / (properties.rowHeight + 1))); 131 132 // For the first run with liquid layout, calculate sizes 133 if(!colSpan) { 134 colSpan = Math.ceil($this.outerWidth(true) / (properties.columnWidth + 1)); 135 rowSpan = Math.ceil($this.outerHeight(true) / (properties.rowHeight + 1)); 136 $this.data('colSpan', colSpan); 137 $this.data('rowSpan', rowSpan); 138 } 139 140 141 /* Do the layout 142 * -------------------------------------------------------------------------------- */ 143 144 // Set spans 145 var aSpan = (isVertical ? colSpan : rowSpan); 146 var bSpan = (isVertical ? rowSpan : colSpan); 147 148 // Bigger tiles can't fit into the last primary (though keep it still at least as 1) 149 var max = Math.max((isVertical ? properties.cols - colSpan : properties.rows - rowSpan) + 1, 1); 150 151 // Loop through/create primaries (set hard limit of 10.000 to prevent endless loop) 152 var a = -1, x = 0, y = 0; 153 while(++a < 10000) { 154 properties.grid[a] = properties.grid[a] || []; 155 156 // Go through the secondaries in the primary, set secondary and tile 157 for (var b = 0; b < max; b++) { 158 var tile = properties.grid[a][b]; 159 160 // If the tile is not free, move to the next one immediately 161 if(tile) { continue; } 162 163 // Tiles spanning to multiple rows/columns - Check if it'll fit 164 var doesFit = true; 165 if(colSpan > 1 || rowSpan > 1) { 166 for (var i = 0; i < aSpan; i++) { 167 for (var j = 0; j < bSpan; j++) { 168 properties.grid[a+j] = properties.grid[a+j] || []; 169 if(properties.grid[a+j][b+i]) { doesFit = false; break; } 170 } 171 172 // If it doesn't fit, don't waste our time looping 173 if(!doesFit) { break; } 174 } 175 } 176 if(!doesFit) { continue } 177 178 179 // Set all the cells as occupied 180 for (var i = 0; i < aSpan; i++) { 181 for (var j = 0; j < bSpan; j++) { 182 properties.grid[a+j][b+i] = true; 183 } 184 } 185 186 // Set x & y values 187 var x = a, y = b; 188 if(isVertical) { var x = b, y = a; } 189 190 // Update container dimensions 191 properties.containerWidth = Math.max(properties.containerWidth, (x + aSpan) * properties.columnWidth); 192 properties.containerHeight = Math.max(properties.containerHeight, (y + bSpan) * properties.rowHeight); 193 194 // In case of liquid layout, set element size full width/height 195 if(instance.options.perfectMasonry.liquid == true) { 196 $this.css({ 197 width: properties.columnWidth * colSpan, 198 height: properties.rowHeight * rowSpan 199 }); 200 } 201 202 // Set the element location and GTFO 203 instance._pushPosition($this, x*properties.columnWidth, y*properties.rowHeight); 204 return; 205 } 206 } 207 208 209 // If we got all the way down to here, the element doesn't fit - Hide it 210 instance._pushPosition($this, -9999, -9999); 211 }); 212 213 // Set row & column count to container 214 var rows = (isVertical ? properties.grid.length : properties.grid[0] && properties.grid[0].length), 215 cols = (isVertical ? properties.grid[0] && properties.grid[0].length : properties.grid.length); 216 $(this.element.context).attr('data-isotope-rows', rows).attr('data-isotope-cols', cols); 217 }, 218 219 220 221 /** 222 * Get container size 223 * 224 * For resizing the container 225 * -------------------------------------------------------------------------------------------------------- */ 226 _perfectMasonryGetContainerSize: function() { 227 return { 228 width: this.perfectMasonry.containerWidth, 229 height: this.perfectMasonry.containerHeight 230 }; 231 }, 232 233 234 235 /** 236 * Resize changed 237 * 238 * Figure out if layout changed 239 * -------------------------------------------------------------------------------------------------------- */ 240 _perfectMasonryResizeChanged: function() { 241 var properties = this.perfectMasonry; 242 243 // Store old values and calculate new numbers 244 var oldCols = properties.cols, 245 oldRows = properties.rows; 246 this._perfectMasonryGetSegments(); 247 248 // If new count was different, force layout change 249 if(this.options.perfectMasonry.layout == 'horizontal' && oldRows !== properties.rows) { return true; } 250 if(oldCols !== properties.cols) { return true; } 251 252 return false;
253 }, 254 255 256 257 258 /** 259 * Private 260 * Do segment calculations by hand 261 * -------------------------------------------------------------------------------------------------------- */ 262 _perfectMasonryGetSegments: function() { 263 var properties = this.perfectMasonry; 264 var parent = this.options.perfectMasonry.parent || this.element.parent(); 265 266 // Calculate columns 267 var parentWidth = parent.width(); 268 properties.cols = Math.floor(parentWidth / properties.columnWidth) || 1; 269 270 // Calculate rows 271 var parentHeight = parent.height(); 272 properties.rows = Math.floor(parentHeight / properties.rowHeight) || 1; 273 } 274 }); 275 276 277})(jQuery); 278
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.