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