File: /home/adventp/www/wp-content/plugins/ut-portfolio/assets/js/jquery.isotope.perfectmasonry.js
/*!
* PerfectMasonry extension for Isotope
*
* Does similar things as the Isotopes "masonry" layoutmode, except that this one will actually go back and plug the holes
* left by bigger elements, thus making a perfect brick wall. Profit!
*
* Usage:
* $('#grid').isotope({
* layoutMode: 'perfectMasonry',
* perfectMasonry: {
* layout: 'horizontal', // Set layout as vertical/horizontal (default: vertical)
* columnWidth: 200, // Set/Prefer columns to x wide (default: width of first tile)
* rowHeight: 200, // Set/Prefer rows to y high (default: height of first tile)
*
* liquid: true, // Set layout as liquid (default: false)
* cols: 3, // Force to have x columns (default: null)
* rows: 3, // Force to have y rows (default: null)
* minCols: 3, // Set min col count (default: 1)
* minRows: 3, // Set min row count (default: 1)
* maxCols: 5, // Set max col count (default: 9999)
* maxRows: 4 // Set max row count (default: 9999)
* }
* });
*
*
* @author Mikko Tikkanen, Zonear Ltd. <contact@zonear.com>
*/
;(function($, undefined) {
var version = '1.2.1';
var isotope = null,
$context = null,
$container = null,
isFirstRun = true;
$.extend($.Isotope.prototype, {
/**
* Reset layout properties
*
* Runs before any layout change
* -------------------------------------------------------------------------------------------------------- */
_perfectMasonryReset: function() {
var isVertical = this.options.perfectMasonry.layout != 'horizontal',
isLiquid = this.options.perfectMasonry.liquid == true;
// Do things on a first run
if(isFirstRun) {
isFirstRun = false;
isotope = this;
$context = $(this.element.context);
$container = $context.parent();
// Make sure we have min/maxCols & rows
this.options.perfectMasonry.minCols = this.options.perfectMasonry.minCols || 1;
this.options.perfectMasonry.minRows = this.options.perfectMasonry.minRows || 1;
this.options.perfectMasonry.maxCols = this.options.perfectMasonry.maxCols || 9999;
this.options.perfectMasonry.maxRows = this.options.perfectMasonry.maxRows || 9999;
}
// For liquid layout, replace default resize handler with forced relayout (outside firstRun as Isotope grid can be destroyed and created on the fly)
if(isLiquid && $._data(window, 'events').smartresize) {
$(window).off('smartresize.isotope');
$(window).on('resize.isotope.perfectmasonry', function() {
if(!$context.hasClass('isotope')) { return; }
$context.isotope('reLayout');
});
}
// Setup layout properties ---------------------------------------------------
var properties = this.perfectMasonry = {};
// Fill properties with columnWidth and rowHeight (true argument)
this._getSegments();
this._getSegments(true);
// ...and with cols & rows
this._perfectMasonryGetSegments();
// Handle liquid layout (cols, rows & sizes must be calculated on the fly)
if(isLiquid) {
var width = $container.width(),
height = $container.height();
// Make sure we have colwidth & rowheight (get it from the calculated )
this.options.perfectMasonry.columnWidth = this.options.perfectMasonry.columnWidth || properties.columnWidth;
this.options.perfectMasonry.rowHeight = this.options.perfectMasonry.rowHeight || properties.rowHeight;
// 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)
properties.cols = this.options.perfectMasonry.cols || Math.floor(width / this.options.perfectMasonry.columnWidth);
properties.rows = this.options.perfectMasonry.rows || Math.floor(height / this.options.perfectMasonry.rowHeight);
properties.cols = Math.min(Math.max(properties.cols, this.options.perfectMasonry.minCols), this.options.perfectMasonry.maxCols);
properties.rows = Math.min(Math.max(properties.rows, this.options.perfectMasonry.minRows), this.options.perfectMasonry.maxRows);
// Recalculate accurate width/height so that the whole available space is used
var diff = (isVertical ? properties.columnWidth / (width / properties.cols) : properties.rowHeight / (height / properties.rows));
properties.columnWidth = Math.floor(properties.columnWidth / diff);
properties.rowHeight = Math.floor(properties.rowHeight / diff);
}
// Create top row of the grid
properties.grid = new Array(this.perfectMasonry.cols);
// Set container dimensions to 0
properties.containerHeight = 0;
properties.containerWidth = 0;
},
/**
* Create layout
* -------------------------------------------------------------------------------------------------------- */
_perfectMasonryLayout: function($elems) {
var instance = this,
properties = this.perfectMasonry,
isVertical = instance.options.perfectMasonry.layout != 'horizontal',
isLiquid = instance.options.perfectMasonry.liquid == true;
// Create first set of the grid
properties.grid = new Array(properties[(isVertical ? 'cols' : 'rows')]);
// Loop each element
$elems.each(function() {
var $this = $(this);
// Calculate col & row spans (with liquid layouts, store desired width as element data)
var colSpan = (isLiquid ? $this.data('colSpan') : Math.ceil($this.outerWidth() / (properties.columnWidth + 1))),
rowSpan = (isLiquid ? $this.data('rowSpan') : Math.ceil($this.outerHeight() / (properties.rowHeight + 1)));
// For the first run with liquid layout, calculate sizes
if(!colSpan) {
colSpan = Math.ceil($this.outerWidth(true) / (properties.columnWidth + 1));
rowSpan = Math.ceil($this.outerHeight(true) / (properties.rowHeight + 1));
$this.data('colSpan', colSpan);
$this.data('rowSpan', rowSpan);
}
/* Do the layout
* -------------------------------------------------------------------------------- */
// Set spans
var aSpan = (isVertical ? colSpan : rowSpan);
var bSpan = (isVertical ? rowSpan : colSpan);
// Bigger tiles can't fit into the last primary (though keep it still at least as 1)
var max = Math.max((isVertical ? properties.cols - colSpan : properties.rows - rowSpan) + 1, 1);
// Loop through/create primaries (set hard limit of 10.000 to prevent endless loop)
var a = -1, x = 0, y = 0;
while(++a < 10000) {
properties.grid[a] = properties.grid[a] || [];
// Go through the secondaries in the primary, set secondary and tile
for (var b = 0; b < max; b++) {
var tile = properties.grid[a][b];
// If the tile is not free, move to the next one immediately
if(tile) { continue; }
// Tiles spanning to multiple rows/columns - Check if it'll fit
var doesFit = true;
if(colSpan > 1 || rowSpan > 1) {
for (var i = 0; i < aSpan; i++) {
for (var j = 0; j < bSpan; j++) {
properties.grid[a+j] = properties.grid[a+j] || [];
if(properties.grid[a+j][b+i]) { doesFit = false; break; }
}
// If it doesn't fit, don't waste our time looping
if(!doesFit) { break; }
}
}
if(!doesFit) { continue }
// Set all the cells as occupied
for (var i = 0; i < aSpan; i++) {
for (var j = 0; j < bSpan; j++) {
properties.grid[a+j][b+i] = true;
}
}
// Set x & y values
var x = a, y = b;
if(isVertical) { var x = b, y = a; }
// Update container dimensions
properties.containerWidth = Math.max(properties.containerWidth, (x + aSpan) * properties.columnWidth);
properties.containerHeight = Math.max(properties.containerHeight, (y + bSpan) * properties.rowHeight);
// In case of liquid layout, set element size full width/height
if(instance.options.perfectMasonry.liquid == true) {
$this.css({
width: properties.columnWidth * colSpan,
height: properties.rowHeight * rowSpan
});
}
// Set the element location and GTFO
instance._pushPosition($this, x*properties.columnWidth, y*properties.rowHeight);
return;
}
}
// If we got all the way down to here, the element doesn't fit - Hide it
instance._pushPosition($this, -9999, -9999);
});
},
/**
* Get container size
*
* For resizing the container
* -------------------------------------------------------------------------------------------------------- */
_perfectMasonryGetContainerSize: function() {
return {
width: this.perfectMasonry.containerWidth,
height: this.perfectMasonry.containerHeight
};
},
/**
* Resize changed
*
* Figure out if layout changed
* -------------------------------------------------------------------------------------------------------- */
_perfectMasonryResizeChanged: function() {
var properties = this.perfectMasonry;
// Store old values and calculate new numbers
var oldCols = properties.cols,
oldRows = properties.rows;
this._perfectMasonryGetSegments();
// If new count was different, force layout change
if(this.options.perfectMasonry.layout == 'horizontal' && oldRows !== properties.rows) { return true; }
if(oldCols !== properties.cols) { return true; }
return false;
},
/**
* Private
* Do segment calculations by hand
* -------------------------------------------------------------------------------------------------------- */
_perfectMasonryGetSegments: function() {
var properties = this.perfectMasonry;
var parent = this.options.perfectMasonry.parent || this.element.parent();
// Calculate columns
var parentWidth = parent.width();
properties.cols = Math.floor(parentWidth / properties.columnWidth) || 1;
// Calculate rows
var parentHeight = parent.height();
properties.rows = Math.floor(parentHeight / properties.rowHeight) || 1;
}
});
})(jQuery);