--- /dev/null
+/**
+ * Isotope v1.5.11
+ * An exquisite jQuery plugin for magical layouts
+ * http://isotope.metafizzy.co
+ *
+ * Commercial use requires one-time license fee
+ * http://metafizzy.co/#licenses
+ *
+ * Copyright 2012 David DeSandro / Metafizzy
+ */
+
+/*jshint curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, undef: true */
+/*global window: true, jQuery: true */
+
+(function( window, $, undefined ){
+
+ // get global vars
+ var document = window.document;
+ var Modernizr = window.Modernizr;
+
+ // helper function
+ var capitalize = function( str ) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ };
+
+ // ========================= getStyleProperty by kangax ===============================
+ // http://perfectionkills.com/feature-testing-css-properties/
+
+ var prefixes = 'Moz Webkit O Ms'.split(' ');
+
+ var getStyleProperty = function( propName ) {
+ var style = document.documentElement.style,
+ prefixed;
+
+ // test standard property first
+ if ( typeof style[propName] === 'string' ) {
+ return propName;
+ }
+
+ // capitalize
+ propName = capitalize( propName );
+
+ // test vendor specific properties
+ for ( var i=0, len = prefixes.length; i < len; i++ ) {
+ prefixed = prefixes[i] + propName;
+ if ( typeof style[ prefixed ] === 'string' ) {
+ return prefixed;
+ }
+ }
+ };
+
+ var transformProp = getStyleProperty('transform'),
+ transitionProp = getStyleProperty('transitionProperty');
+
+
+ // ========================= miniModernizr ===============================
+ // <3<3<3 and thanks to Faruk and Paul for doing the heavy lifting
+
+ /*!
+ * Modernizr v1.6ish: miniModernizr for Isotope
+ * http://www.modernizr.com
+ *
+ * Developed by:
+ * - Faruk Ates http://farukat.es/
+ * - Paul Irish http://paulirish.com/
+ *
+ * Copyright (c) 2009-2010
+ * Dual-licensed under the BSD or MIT licenses.
+ * http://www.modernizr.com/license/
+ */
+
+ /*
+ * This version whittles down the script just to check support for
+ * CSS transitions, transforms, and 3D transforms.
+ */
+
+ var tests = {
+ csstransforms: function() {
+ return !!transformProp;
+ },
+
+ csstransforms3d: function() {
+ var test = !!getStyleProperty('perspective');
+ // double check for Chrome's false positive
+ if ( test ) {
+ var vendorCSSPrefixes = ' -o- -moz- -ms- -webkit- -khtml- '.split(' '),
+ mediaQuery = '@media (' + vendorCSSPrefixes.join('transform-3d),(') + 'modernizr)',
+ $style = $('<style>' + mediaQuery + '{#modernizr{height:3px}}' + '</style>')
+ .appendTo('head'),
+ $div = $('<div id="modernizr" />').appendTo('html');
+
+ test = $div.height() === 3;
+
+ $div.remove();
+ $style.remove();
+ }
+ return test;
+ },
+
+ csstransitions: function() {
+ return !!transitionProp;
+ }
+ };
+
+ var testName;
+
+ if ( Modernizr ) {
+ // if there's a previous Modernzir, check if there are necessary tests
+ for ( testName in tests) {
+ if ( !Modernizr.hasOwnProperty( testName ) ) {
+ // if test hasn't been run, use addTest to run it
+ Modernizr.addTest( testName, tests[ testName ] );
+ }
+ }
+ } else {
+ // or create new mini Modernizr that just has the 3 tests
+ Modernizr = window.Modernizr = {
+ _version : '1.6ish: miniModernizr for Isotope'
+ };
+
+ var classes = ' ';
+ var result;
+
+ // Run through tests
+ for ( testName in tests) {
+ result = tests[ testName ]();
+ Modernizr[ testName ] = result;
+ classes += ' ' + ( result ? '' : 'no-' ) + testName;
+ }
+
+ // Add the new classes to the <html> element.
+ $('html').addClass( classes );
+ }
+
+
+ // ========================= isoTransform ===============================
+
+ /**
+ * provides hooks for .css({ scale: value, translate: [x, y] })
+ * Progressively enhanced CSS transforms
+ * Uses hardware accelerated 3D transforms for Safari
+ * or falls back to 2D transforms.
+ */
+
+ if ( Modernizr.csstransforms ) {
+
+ // i.e. transformFnNotations.scale(0.5) >> 'scale3d( 0.5, 0.5, 1)'
+ var transformFnNotations = Modernizr.csstransforms3d ?
+ { // 3D transform functions
+ translate : function ( position ) {
+ return 'translate3d(' + position[0] + 'px, ' + position[1] + 'px, 0) ';
+ },
+ scale : function ( scale ) {
+ return 'scale3d(' + scale + ', ' + scale + ', 1) ';
+ }
+ } :
+ { // 2D transform functions
+ translate : function ( position ) {
+ return 'translate(' + position[0] + 'px, ' + position[1] + 'px) ';
+ },
+ scale : function ( scale ) {
+ return 'scale(' + scale + ') ';
+ }
+ }
+ ;
+
+ var setIsoTransform = function ( elem, name, value ) {
+ // unpack current transform data
+ var data = $.data( elem, 'isoTransform' ) || {},
+ newData = {},
+ fnName,
+ transformObj = {},
+ transformValue;
+
+ // i.e. newData.scale = 0.5
+ newData[ name ] = value;
+ // extend new value over current data
+ $.extend( data, newData );
+
+ for ( fnName in data ) {
+ transformValue = data[ fnName ];
+ transformObj[ fnName ] = transformFnNotations[ fnName ]( transformValue );
+ }
+
+ // get proper order
+ // ideally, we could loop through this give an array, but since we only have
+ // a couple transforms we're keeping track of, we'll do it like so
+ var translateFn = transformObj.translate || '',
+ scaleFn = transformObj.scale || '',
+ // sorting so translate always comes first
+ valueFns = translateFn + scaleFn;
+
+ // set data back in elem
+ $.data( elem, 'isoTransform', data );
+
+ // set name to vendor specific property
+ elem.style[ transformProp ] = valueFns;
+ };
+
+ // ==================== scale ===================
+
+ $.cssNumber.scale = true;
+
+ $.cssHooks.scale = {
+ set: function( elem, value ) {
+ // uncomment this bit if you want to properly parse strings
+ // if ( typeof value === 'string' ) {
+ // value = parseFloat( value );
+ // }
+ setIsoTransform( elem, 'scale', value );
+ },
+ get: function( elem, computed ) {
+ var transform = $.data( elem, 'isoTransform' );
+ return transform && transform.scale ? transform.scale : 1;
+ }
+ };
+
+ $.fx.step.scale = function( fx ) {
+ $.cssHooks.scale.set( fx.elem, fx.now+fx.unit );
+ };
+
+
+ // ==================== translate ===================
+
+ $.cssNumber.translate = true;
+
+ $.cssHooks.translate = {
+ set: function( elem, value ) {
+
+ // uncomment this bit if you want to properly parse strings
+ // if ( typeof value === 'string' ) {
+ // value = value.split(' ');
+ // }
+ //
+ // var i, val;
+ // for ( i = 0; i < 2; i++ ) {
+ // val = value[i];
+ // if ( typeof val === 'string' ) {
+ // val = parseInt( val );
+ // }
+ // }
+
+ setIsoTransform( elem, 'translate', value );
+ },
+
+ get: function( elem, computed ) {
+ var transform = $.data( elem, 'isoTransform' );
+ return transform && transform.translate ? transform.translate : [ 0, 0 ];
+ }
+ };
+
+ }
+
+ // ========================= get transition-end event ===============================
+ var transitionEndEvent, transitionDurProp;
+
+ if ( Modernizr.csstransitions ) {
+ transitionEndEvent = {
+ WebkitTransitionProperty: 'webkitTransitionEnd', // webkit
+ MozTransitionProperty: 'transitionend',
+ OTransitionProperty: 'oTransitionEnd',
+ transitionProperty: 'transitionEnd'
+ }[ transitionProp ];
+
+ transitionDurProp = getStyleProperty('transitionDuration');
+ }
+
+ // ========================= smartresize ===============================
+
+ /*
+ * smartresize: debounced resize event for jQuery
+ *
+ * latest version and complete README available on Github:
+ * https://github.com/louisremi/jquery.smartresize.js
+ *
+ * Copyright 2011 @louis_remi
+ * Licensed under the MIT license.
+ */
+
+ var $event = $.event,
+ resizeTimeout;
+
+ $event.special.smartresize = {
+ setup: function() {
+ $(this).bind( "resize", $event.special.smartresize.handler );
+ },
+ teardown: function() {
+ $(this).unbind( "resize", $event.special.smartresize.handler );
+ },
+ handler: function( event, execAsap ) {
+ // Save the context
+ var context = this,
+ args = arguments;
+
+ // set correct event type
+ event.type = "smartresize";
+
+ if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
+ resizeTimeout = setTimeout(function() {
+ jQuery.event.handle.apply( context, args );
+ }, execAsap === "execAsap"? 0 : 100 );
+ }
+ };
+
+ $.fn.smartresize = function( fn ) {
+ return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
+ };
+
+
+
+// ========================= Isotope ===============================
+
+
+ // our "Widget" object constructor
+ $.Isotope = function( options, element, callback ){
+ this.element = $( element );
+
+ this._create( options );
+ this._init( callback );
+ };
+
+ // styles of container element we want to keep track of
+ var isoContainerStyles = [ 'overflow', 'position', 'width', 'height' ];
+
+ var $window = $(window);
+
+ $.Isotope.settings = {
+ resizable: true,
+ layoutMode : 'masonry',
+ containerClass : 'isotope',
+ itemClass : 'isotope-item',
+ hiddenClass : 'isotope-hidden',
+ hiddenStyle: { opacity: 0, scale: 0.001 },
+ visibleStyle: { opacity: 1, scale: 1 },
+ animationEngine: 'best-available',
+ animationOptions: {
+ queue: false,
+ duration: 800
+ },
+ sortBy : 'original-order',
+ sortAscending : true,
+ resizesContainer : true,
+ transformsEnabled: !$.browser.opera, // disable transforms in Opera
+ itemPositionDataEnabled: false
+ };
+
+ $.Isotope.prototype = {
+
+ // sets up widget
+ _create : function( options ) {
+
+ this.options = $.extend( {}, $.Isotope.settings, options );
+
+ this.styleQueue = [];
+ this.elemCount = 0;
+
+ // get original styles in case we re-apply them in .destroy()
+ var elemStyle = this.element[0].style;
+ this.originalStyle = {};
+ for ( var i=0, len = isoContainerStyles.length; i < len; i++ ) {
+ var prop = isoContainerStyles[i];
+ this.originalStyle[ prop ] = elemStyle[ prop ] || '';
+ }
+
+ this.element.css({
+ overflow : 'hidden',
+ position : 'relative'
+ });
+
+ this._updateAnimationEngine();
+ this._updateUsingTransforms();
+
+ // sorting
+ var originalOrderSorter = {
+ 'original-order' : function( $elem, instance ) {
+ instance.elemCount ++;
+ return instance.elemCount;
+ },
+ random : function() {
+ return Math.random();
+ }
+ };
+
+ this.options.getSortData = $.extend( this.options.getSortData, originalOrderSorter );
+
+ // need to get atoms
+ this.reloadItems();
+
+ // get top left position of where the bricks should be
+ this.offset = {
+ left: parseInt( this.element.css('padding-left'), 10 ),
+ top: parseInt( this.element.css('padding-top'), 10 )
+ };
+
+ // add isotope class first time around
+ var instance = this;
+ setTimeout( function() {
+ instance.element.addClass( instance.options.containerClass );
+ }, 0 );
+
+ // bind resize method
+ if ( this.options.resizable ) {
+ $window.bind( 'smartresize.isotope', function() {
+ instance.resize();
+ });
+ }
+
+ // dismiss all click events from hidden events
+ this.element.delegate( '.' + this.options.hiddenClass, 'click', function(){
+ return false;
+ });
+
+ },
+
+ _getAtoms : function( $elems ) {
+ var selector = this.options.itemSelector,
+ // filter & find
+ $atoms = selector ? $elems.filter( selector ).add( $elems.find( selector ) ) : $elems,
+ // base style for atoms
+ atomStyle = { position: 'absolute' };
+
+ if ( this.usingTransforms ) {
+ atomStyle.left = 0;
+ atomStyle.top = 0;
+ }
+
+ $atoms.css( atomStyle ).addClass( this.options.itemClass );
+
+ this.updateSortData( $atoms, true );
+
+ return $atoms;
+ },
+
+ // _init fires when your instance is first created
+ // (from the constructor above), and when you
+ // attempt to initialize the widget again (by the bridge)
+ // after it has already been initialized.
+ _init : function( callback ) {
+
+ this.$filteredAtoms = this._filter( this.$allAtoms );
+ this._sort();
+ this.reLayout( callback );
+
+ },
+
+ option : function( opts ){
+ // change options AFTER initialization:
+ // signature: $('#foo').bar({ cool:false });
+ if ( $.isPlainObject( opts ) ){
+ this.options = $.extend( true, this.options, opts );
+
+ // trigger _updateOptionName if it exists
+ var updateOptionFn;
+ for ( var optionName in opts ) {
+ updateOptionFn = '_update' + capitalize( optionName );
+ if ( this[ updateOptionFn ] ) {
+ this[ updateOptionFn ]();
+ }
+ }
+ }
+ },
+
+ // ====================== updaters ====================== //
+ // kind of like setters
+
+ _updateAnimationEngine : function() {
+ var animationEngine = this.options.animationEngine.toLowerCase().replace( /[ _\-]/g, '');
+ var isUsingJQueryAnimation;
+ // set applyStyleFnName
+ switch ( animationEngine ) {
+ case 'css' :
+ case 'none' :
+ isUsingJQueryAnimation = false;
+ break;
+ case 'jquery' :
+ isUsingJQueryAnimation = true;
+ break;
+ default : // best available
+ isUsingJQueryAnimation = !Modernizr.csstransitions;
+ }
+ this.isUsingJQueryAnimation = isUsingJQueryAnimation;
+ this._updateUsingTransforms();
+ },
+
+ _updateTransformsEnabled : function() {
+ this._updateUsingTransforms();
+ },
+
+ _updateUsingTransforms : function() {
+ var usingTransforms = this.usingTransforms = this.options.transformsEnabled &&
+ Modernizr.csstransforms && Modernizr.csstransitions && !this.isUsingJQueryAnimation;
+
+ // prevent scales when transforms are disabled
+ if ( !usingTransforms ) {
+ delete this.options.hiddenStyle.scale;
+ delete this.options.visibleStyle.scale;
+ }
+
+ this.getPositionStyles = usingTransforms ? this._translate : this._positionAbs;
+ },
+
+
+ // ====================== Filtering ======================
+
+ _filter : function( $atoms ) {
+ var filter = this.options.filter === '' ? '*' : this.options.filter;
+
+ if ( !filter ) {
+ return $atoms;
+ }
+
+ var hiddenClass = this.options.hiddenClass,
+ hiddenSelector = '.' + hiddenClass,
+ $hiddenAtoms = $atoms.filter( hiddenSelector ),
+ $atomsToShow = $hiddenAtoms;
+
+ if ( filter !== '*' ) {
+ $atomsToShow = $hiddenAtoms.filter( filter );
+ var $atomsToHide = $atoms.not( hiddenSelector ).not( filter ).addClass( hiddenClass );
+ this.styleQueue.push({ $el: $atomsToHide, style: this.options.hiddenStyle });
+ }
+
+ this.styleQueue.push({ $el: $atomsToShow, style: this.options.visibleStyle });
+ $atomsToShow.removeClass( hiddenClass );
+
+ return $atoms.filter( filter );
+ },
+
+ // ====================== Sorting ======================
+
+ updateSortData : function( $atoms, isIncrementingElemCount ) {
+ var instance = this,
+ getSortData = this.options.getSortData,
+ $this, sortData;
+ $atoms.each(function(){
+ $this = $(this);
+ sortData = {};
+ // get value for sort data based on fn( $elem ) passed in
+ for ( var key in getSortData ) {
+ if ( !isIncrementingElemCount && key === 'original-order' ) {
+ // keep original order original
+ sortData[ key ] = $.data( this, 'isotope-sort-data' )[ key ];
+ } else {
+ sortData[ key ] = getSortData[ key ]( $this, instance );
+ }
+ }
+ // apply sort data to element
+ $.data( this, 'isotope-sort-data', sortData );
+ });
+ },
+
+ // used on all the filtered atoms
+ _sort : function() {
+
+ var sortBy = this.options.sortBy,
+ getSorter = this._getSorter,
+ sortDir = this.options.sortAscending ? 1 : -1,
+ sortFn = function( alpha, beta ) {
+ var a = getSorter( alpha, sortBy ),
+ b = getSorter( beta, sortBy );
+ // fall back to original order if data matches
+ if ( a === b && sortBy !== 'original-order') {
+ a = getSorter( alpha, 'original-order' );
+ b = getSorter( beta, 'original-order' );
+ }
+ return ( ( a > b ) ? 1 : ( a < b ) ? -1 : 0 ) * sortDir;
+ };
+
+ this.$filteredAtoms.sort( sortFn );
+ },
+
+ _getSorter : function( elem, sortBy ) {
+ return $.data( elem, 'isotope-sort-data' )[ sortBy ];
+ },
+
+ // ====================== Layout Helpers ======================
+
+ _translate : function( x, y ) {
+ return { translate : [ x, y ] };
+ },
+
+ _positionAbs : function( x, y ) {
+ return { left: x, top: y };
+ },
+
+ _pushPosition : function( $elem, x, y ) {
+ x += this.offset.left;
+ y += this.offset.top;
+ var position = this.getPositionStyles( x, y );
+ this.styleQueue.push({ $el: $elem, style: position });
+ if ( this.options.itemPositionDataEnabled ) {
+ $elem.data('isotope-item-position', {x: x, y: y} );
+ }
+ },
+
+
+ // ====================== General Layout ======================
+
+ // used on collection of atoms (should be filtered, and sorted before )
+ // accepts atoms-to-be-laid-out to start with
+ layout : function( $elems, callback ) {
+
+ var layoutMode = this.options.layoutMode;
+
+ // layout logic
+ this[ '_' + layoutMode + 'Layout' ]( $elems );
+
+ // set the size of the container
+ if ( this.options.resizesContainer ) {
+ var containerStyle = this[ '_' + layoutMode + 'GetContainerSize' ]();
+ this.styleQueue.push({ $el: this.element, style: containerStyle });
+ }
+
+ this._processStyleQueue( $elems, callback );
+
+ this.isLaidOut = true;
+ },
+
+ _processStyleQueue : function( $elems, callback ) {
+ // are we animating the layout arrangement?
+ // use plugin-ish syntax for css or animate
+ var styleFn = !this.isLaidOut ? 'css' : (
+ this.isUsingJQueryAnimation ? 'animate' : 'css'
+ ),
+ animOpts = this.options.animationOptions,
+ onLayout = this.options.onLayout,
+ objStyleFn, processor,
+ triggerCallbackNow, callbackFn;
+
+ // default styleQueue processor, may be overwritten down below
+ processor = function( i, obj ) {
+ obj.$el[ styleFn ]( obj.style, animOpts );
+ };
+
+ if ( this._isInserting && this.isUsingJQueryAnimation ) {
+ // if using styleQueue to insert items
+ processor = function( i, obj ) {
+ // only animate if it not being inserted
+ objStyleFn = obj.$el.hasClass('no-transition') ? 'css' : styleFn;
+ obj.$el[ objStyleFn ]( obj.style, animOpts );
+ };
+
+ } else if ( callback || onLayout || animOpts.complete ) {
+ // has callback
+ var isCallbackTriggered = false,
+ // array of possible callbacks to trigger
+ callbacks = [ callback, onLayout, animOpts.complete ],
+ instance = this;
+ triggerCallbackNow = true;
+ // trigger callback only once
+ callbackFn = function() {
+ if ( isCallbackTriggered ) {
+ return;
+ }
+ var hollaback;
+ for (var i=0, len = callbacks.length; i < len; i++) {
+ hollaback = callbacks[i];
+ if ( typeof hollaback === 'function' ) {
+ hollaback.call( instance.element, $elems );
+ }
+ }
+ isCallbackTriggered = true;
+ };
+
+ if ( this.isUsingJQueryAnimation && styleFn === 'animate' ) {
+ // add callback to animation options
+ animOpts.complete = callbackFn;
+ triggerCallbackNow = false;
+
+ } else if ( Modernizr.csstransitions ) {
+ // detect if first item has transition
+ var i = 0,
+ testElem = this.styleQueue[0].$el,
+ styleObj;
+ // get first non-empty jQ object
+ while ( !testElem.length ) {
+ styleObj = this.styleQueue[ i++ ];
+ // HACK: sometimes styleQueue[i] is undefined
+ if ( !styleObj ) {
+ return;
+ }
+ testElem = styleObj.$el;
+ }
+ // get transition duration of the first element in that object
+ // yeah, this is inexact
+ var duration = parseFloat( getComputedStyle( testElem[0] )[ transitionDurProp ] );
+ if ( duration > 0 ) {
+ processor = function( i, obj ) {
+ obj.$el[ styleFn ]( obj.style, animOpts )
+ // trigger callback at transition end
+ .one( transitionEndEvent, callbackFn );
+ };
+ triggerCallbackNow = false;
+ }
+ }
+ }
+
+ // process styleQueue
+ $.each( this.styleQueue, processor );
+
+ if ( triggerCallbackNow ) {
+ callbackFn();
+ }
+
+ // clear out queue for next time
+ this.styleQueue = [];
+ },
+
+
+ resize : function() {
+ if ( this[ '_' + this.options.layoutMode + 'ResizeChanged' ]() ) {
+ this.reLayout();
+ }
+ },
+
+
+ reLayout : function( callback ) {
+
+ this[ '_' + this.options.layoutMode + 'Reset' ]();
+ this.layout( this.$filteredAtoms, callback );
+
+ },
+
+ // ====================== Convenience methods ======================
+
+ // ====================== Adding items ======================
+
+ // adds a jQuery object of items to a isotope container
+ addItems : function( $content, callback ) {
+ var $newAtoms = this._getAtoms( $content );
+ // add new atoms to atoms pools
+ this.$allAtoms = this.$allAtoms.add( $newAtoms );
+
+ if ( callback ) {
+ callback( $newAtoms );
+ }
+ },
+
+ // convienence method for adding elements properly to any layout
+ // positions items, hides them, then animates them back in <--- very sezzy
+ insert : function( $content, callback ) {
+ // position items
+ this.element.append( $content );
+
+ var instance = this;
+ this.addItems( $content, function( $newAtoms ) {
+ var $newFilteredAtoms = instance._filter( $newAtoms );
+ instance._addHideAppended( $newFilteredAtoms );
+ instance._sort();
+ instance.reLayout();
+ instance._revealAppended( $newFilteredAtoms, callback );
+ });
+
+ },
+
+ // convienence method for working with Infinite Scroll
+ appended : function( $content, callback ) {
+ var instance = this;
+ this.addItems( $content, function( $newAtoms ) {
+ instance._addHideAppended( $newAtoms );
+ instance.layout( $newAtoms );
+ instance._revealAppended( $newAtoms, callback );
+ });
+ },
+
+ // adds new atoms, then hides them before positioning
+ _addHideAppended : function( $newAtoms ) {
+ this.$filteredAtoms = this.$filteredAtoms.add( $newAtoms );
+ $newAtoms.addClass('no-transition');
+
+ this._isInserting = true;
+
+ // apply hidden styles
+ this.styleQueue.push({ $el: $newAtoms, style: this.options.hiddenStyle });
+ },
+
+ // sets visible style on new atoms
+ _revealAppended : function( $newAtoms, callback ) {
+ var instance = this;
+ // apply visible style after a sec
+ setTimeout( function() {
+ // enable animation
+ $newAtoms.removeClass('no-transition');
+ // reveal newly inserted filtered elements
+ instance.styleQueue.push({ $el: $newAtoms, style: instance.options.visibleStyle });
+ instance._isInserting = false;
+ instance._processStyleQueue( $newAtoms, callback );
+ }, 10 );
+ },
+
+ // gathers all atoms
+ reloadItems : function() {
+ this.$allAtoms = this._getAtoms( this.element.children() );
+ },
+
+ // removes elements from Isotope widget
+ remove: function( $content, callback ) {
+ // remove elements from Isotope instance in callback
+ var instance = this;
+ var removeContent = function() {
+ instance.$allAtoms = instance.$allAtoms.not( $content );
+ $content.remove();
+ };
+
+ if ( $content.filter( ':not(.' + this.options.hiddenClass + ')' ).length ) {
+ // if any non-hidden content needs to be removed
+ this.styleQueue.push({ $el: $content, style: this.options.hiddenStyle });
+ this.$filteredAtoms = this.$filteredAtoms.not( $content );
+ this._sort();
+ this.reLayout( removeContent, callback );
+ } else {
+ // remove it now
+ removeContent();
+ if ( callback ) {
+ callback.call( this.element );
+ }
+ }
+
+ },
+
+ shuffle : function( callback ) {
+ this.updateSortData( this.$allAtoms );
+ this.options.sortBy = 'random';
+ this._sort();
+ this.reLayout( callback );
+ },
+
+ // destroys widget, returns elements and container back (close) to original style
+ destroy : function() {
+
+ var usingTransforms = this.usingTransforms;
+ var options = this.options;
+
+ this.$allAtoms
+ .removeClass( options.hiddenClass + ' ' + options.itemClass )
+ .each(function(){
+ var style = this.style;
+ style.position = '';
+ style.top = '';
+ style.left = '';
+ style.opacity = '';
+ if ( usingTransforms ) {
+ style[ transformProp ] = '';
+ }
+ });
+
+ // re-apply saved container styles
+ var elemStyle = this.element[0].style;
+ for ( var i=0, len = isoContainerStyles.length; i < len; i++ ) {
+ var prop = isoContainerStyles[i];
+ elemStyle[ prop ] = this.originalStyle[ prop ];
+ }
+
+ this.element
+ .unbind('.isotope')
+ .undelegate( '.' + options.hiddenClass, 'click' )
+ .removeClass( options.containerClass )
+ .removeData('isotope');
+
+ $window.unbind('.isotope');
+
+ },
+
+
+ // ====================== LAYOUTS ======================
+
+ // calculates number of rows or columns
+ // requires columnWidth or rowHeight to be set on namespaced object
+ // i.e. this.masonry.columnWidth = 200
+ _getSegments : function( isRows ) {
+ var namespace = this.options.layoutMode,
+ measure = isRows ? 'rowHeight' : 'columnWidth',
+ size = isRows ? 'height' : 'width',
+ segmentsName = isRows ? 'rows' : 'cols',
+ containerSize = this.element[ size ](),
+ segments,
+ // i.e. options.masonry && options.masonry.columnWidth
+ segmentSize = this.options[ namespace ] && this.options[ namespace ][ measure ] ||
+ // or use the size of the first item, i.e. outerWidth
+ this.$filteredAtoms[ 'outer' + capitalize(size) ](true) ||
+ // if there's no items, use size of container
+ containerSize;
+
+ segments = Math.floor( containerSize / segmentSize );
+ segments = Math.max( segments, 1 );
+
+ // i.e. this.masonry.cols = ....
+ this[ namespace ][ segmentsName ] = segments;
+ // i.e. this.masonry.columnWidth = ...
+ this[ namespace ][ measure ] = segmentSize;
+
+ },
+
+ _checkIfSegmentsChanged : function( isRows ) {
+ var namespace = this.options.layoutMode,
+ segmentsName = isRows ? 'rows' : 'cols',
+ prevSegments = this[ namespace ][ segmentsName ];
+ // update cols/rows
+ this._getSegments( isRows );
+ // return if updated cols/rows is not equal to previous
+ return ( this[ namespace ][ segmentsName ] !== prevSegments );
+ },
+
+ // ====================== Masonry ======================
+
+ _masonryReset : function() {
+ // layout-specific props
+ this.masonry = {};
+ // FIXME shouldn't have to call this again
+ this._getSegments();
+ var i = this.masonry.cols;
+ this.masonry.colYs = [];
+ while (i--) {
+ this.masonry.colYs.push( 0 );
+ }
+ },
+
+ _masonryLayout : function( $elems ) {
+ var instance = this,
+ props = instance.masonry;
+ $elems.each(function(){
+ var $this = $(this),
+ //how many columns does this brick span
+ colSpan = Math.ceil( $this.outerWidth(true) / props.columnWidth );
+ colSpan = Math.min( colSpan, props.cols );
+
+ if ( colSpan === 1 ) {
+ // if brick spans only one column, just like singleMode
+ instance._masonryPlaceBrick( $this, props.colYs );
+ } else {
+ // brick spans more than one column
+ /