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