text: /**
   * Isotope v1.5.25
   * 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 asi: true, browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */
  /*global jQuery: false */
  
  (function( window, $, undefined ){
  
    'use strict';
  
    // 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() === 6;
  
          $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 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,
        dispatchMethod = $.event.handle ? 'handle' : 'dispatch',
        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() {
          $event[ dispatchMethod ].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 = [ '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 },
      containerStyle: {
        position: 'relative',
        overflow: 'hidden'
      },
      animationEngine: 'best-available',
      animationOptions: {
        queue: false,
        duration: 800
      },
      sortBy : 'original-order',
      sortAscending : true,
      resizesContainer : true,
      transformsEnabled: true,
      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 = {};
        // keep track of container styles
        var containerStyles = isoContainerStyles.slice(0);
        for ( var prop in this.options.containerStyle ) {
          containerStyles.push( prop );
        }
        for ( var i=0, len = containerStyles.length; i < len; i++ ) {
          prop = containerStyles[i];
          this.originalStyle[ prop ] = elemStyle[ prop ] || '';
        }
        // apply container style from options
        this.element.css( this.options.containerStyle );
  
        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') || 0 ), 10 ),
          top: parseInt( ( this.element.css('padding-top') || 0 ), 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' };
  
        // filter out text nodes
        $atoms = $atoms.filter( function( i, atom ) {
          return atom.nodeType === 1;
        });
  
        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 = Math.round( x + this.offset.left );
        y = Math.round( 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, instance );
              }
            }
            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,
                firstItem = this.styleQueue[0],
                testElem = firstItem && firstItem.$el,
                styleObj;
            // get first non-empty jQ object
            while ( !testElem || !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 immediately from Isotope instance
        this.$allAtoms = this.$allAtoms.not( $content );
        this.$filteredAtoms = this.$filteredAtoms.not( $content );
        // remove() as a callback, for after transition / animation
        var instance = this;
        var removeContent = function() {
          $content.remove();
          if ( callback ) {
            callback.call( instance.element );
          }
        };
  
        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._sort();
          this.reLayout( removeContent );
        } else {
          // remove it now
          removeContent();
        }
  
      },
  
      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 prop in this.originalStyle ) {
          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
            // how many different places could this brick fit horizontally
            var groupCount = props.cols + 1 - colSpan,
                groupY = [],
                groupColY,
                i;
  
            // for each group potential horizontal position
            for ( i=0; i < groupCount; i++ ) {
              // make an array of colY values for that one group
              groupColY = props.colYs.slice( i, i+colSpan );
              // and get the max value of the array
              groupY[i] = Math.max.apply( Math, groupColY );
            }
  
            instance._masonryPlaceBrick( $this, groupY );
          }
        });
      },
  
      // worker method that places brick in the columnSet
      //   with the the minY
      _masonryPlaceBrick : function( $brick, setY ) {
        // get the minimum Y value from the columns
        var minimumY = Math.min.apply( Math, setY ),
            shortCol = 0;
  
        // Find index of short column, the first from the left
        for (var i=0, len = setY.length; i < len; i++) {
          if ( setY[i] === minimumY ) {
            shortCol = i;
            break;
          }
        }
  
        // position the brick
        var x = this.masonry.columnWidth * shortCol,
            y = minimumY;
        this._pushPosition( $brick, x, y );
  
        // apply setHeight to necessary columns
        var setHeight = minimumY + $brick.outerHeight(true),
            setSpan = this.masonry.cols + 1 - len;
        for ( i=0; i < setSpan; i++ ) {
          this.masonry.colYs[ shortCol + i ] = setHeight;
        }
  
      },
  
      _masonryGetContainerSize : function() {
        var containerHeight = Math.max.apply( Math, this.masonry.colYs );
        return { height: containerHeight };
      },
  
      _masonryResizeChanged : function() {
        return this._checkIfSegmentsChanged();
      },
  
      // ====================== fitRows ======================
  
      _fitRowsReset : function() {
        this.fitRows = {
          x : 0,
          y : 0,
          height : 0
        };
      },
  
      _fitRowsLayout : function( $elems ) {
        var instance = this,
            containerWidth = this.element.width(),
            props = this.fitRows;
  
        $elems.each( function() {
          var $this = $(this),
              atomW = $this.outerWidth(true),
              atomH = $this.outerHeight(true);
  
          if ( props.x !== 0 && atomW + props.x > containerWidth ) {
            // if this element cannot fit in the current row
            props.x = 0;
            props.y = props.height;
          }
  
          // position the atom
          instance._pushPosition( $this, props.x, props.y );
  
          props.height = Math.max( props.y + atomH, props.height );
          props.x += atomW;
  
        });
      },
  
      _fitRowsGetContainerSize : function () {
        return { height : this.fitRows.height };
      },
  
      _fitRowsResizeChanged : function() {
        return true;
      },
  
  
      // ====================== cellsByRow ======================
  
      _cellsByRowReset : function() {
        this.cellsByRow = {
          index : 0
        };
        // get this.cellsByRow.columnWidth
        this._getSegments();
        // get this.cellsByRow.rowHeight
        this._getSegments(true);
      },
  
      _cellsByRowLayout : function( $elems ) {
        var instance = this,
            props = this.cellsByRow;
        $elems.each( function(){
          var $this = $(this),
              col = props.index % props.cols,
              row = Math.floor( props.index / props.cols ),
              x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2,
              y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2;
          instance._pushPosition( $this, x, y );
          props.index ++;
        });
      },
  
      _cellsByRowGetContainerSize : function() {
        return { height : Math.ceil( this.$filteredAtoms.length / this.cellsByRow.cols ) * this.cellsByRow.rowHeight + this.offset.top };
      },
  
      _cellsByRowResizeChanged : function() {
        return this._checkIfSegmentsChanged();
      },
  
  
      // ====================== straightDown ======================
  
      _straightDownReset : function() {
        this.straightDown = {
          y : 0
        };
      },
  
      _straightDownLayout : function( $elems ) {
        var instance = this;
        $elems.each( function( i ){
          var $this = $(this);
          instance._pushPosition( $this, 0, instance.straightDown.y );
          instance.straightDown.y += $this.outerHeight(true);
        });
      },
  
      _straightDownGetContainerSize : function() {
        return { height : this.straightDown.y };
      },
  
      _straightDownResizeChanged : function() {
        return true;
      },
  
  
      // ====================== masonryHorizontal ======================
  
      _masonryHorizontalReset : function() {
        // layout-specific props
        this.masonryHorizontal = {};
        // FIXME shouldn't have to call this again
        this._getSegments( true );
        var i = this.masonryHorizontal.rows;
        this.masonryHorizontal.rowXs = [];
        while (i--) {
          this.masonryHorizontal.rowXs.push( 0 );
        }
      },
  
      _masonryHorizontalLayout : function( $elems ) {
        var instance = this,
            props = instance.masonryHorizontal;
        $elems.each(function(){
          var $this  = $(this),
              //how many rows does this brick span
              rowSpan = Math.ceil( $this.outerHeight(true) / props.rowHeight );
          rowSpan = Math.min( rowSpan, props.rows );
  
          if ( rowSpan === 1 ) {
            // if brick spans only one column, just like singleMode
            instance._masonryHorizontalPlaceBrick( $this, props.rowXs );
          } else {
            // brick spans more than one row
            // how many different places could this brick fit horizontally
            var groupCount = props.rows + 1 - rowSpan,
                groupX = [],
                groupRowX, i;
  
            // for each group potential horizontal position
            for ( i=0; i < groupCount; i++ ) {
              // make an array of colY values for that one group
              groupRowX = props.rowXs.slice( i, i+rowSpan );
              // and get the max value of the array
              groupX[i] = Math.max.apply( Math, groupRowX );
            }
  
            instance._masonryHorizontalPlaceBrick( $this, groupX );
          }
        });
      },
  
      _masonryHorizontalPlaceBrick : function( $brick, setX ) {
        // get the minimum Y value from the columns
        var minimumX  = Math.min.apply( Math, setX ),
            smallRow  = 0;
        // Find index of smallest row, the first from the top
        for (var i=0, len = setX.length; i < len; i++) {
          if ( setX[i] === minimumX ) {
            smallRow = i;
            break;
          }
        }
  
        // position the brick
        var x = minimumX,
            y = this.masonryHorizontal.rowHeight * smallRow;
        this._pushPosition( $brick, x, y );
  
        // apply setHeight to necessary columns
        var setWidth = minimumX + $brick.outerWidth(true),
            setSpan = this.masonryHorizontal.rows + 1 - len;
        for ( i=0; i < setSpan; i++ ) {
          this.masonryHorizontal.rowXs[ smallRow + i ] = setWidth;
        }
      },
  
      _masonryHorizontalGetContainerSize : function() {
        var containerWidth = Math.max.apply( Math, this.masonryHorizontal.rowXs );
        return { width: containerWidth };
      },
  
      _masonryHorizontalResizeChanged : function() {
        return this._checkIfSegmentsChanged(true);
      },
  
  
      // ====================== fitColumns ======================
  
      _fitColumnsReset : function() {
        this.fitColumns = {
          x : 0,
          y : 0,
          width : 0
        };
      },
  
      _fitColumnsLayout : function( $elems ) {
        var instance = this,
            containerHeight = this.element.height(),
            props = this.fitColumns;
        $elems.each( function() {
          var $this = $(this),
              atomW = $this.outerWidth(true),
              atomH = $this.outerHeight(true);
  
          if ( props.y !== 0 && atomH + props.y > containerHeight ) {
            // if this element cannot fit in the current column
            props.x = props.width;
            props.y = 0;
          }
  
          // position the atom
          instance._pushPosition( $this, props.x, props.y );
  
          props.width = Math.max( props.x + atomW, props.width );
          props.y += atomH;
  
        });
      },
  
      _fitColumnsGetContainerSize : function () {
        return { width : this.fitColumns.width };
      },
  
      _fitColumnsResizeChanged : function() {
        return true;
      },
  
  
  
      // ====================== cellsByColumn ======================
  
      _cellsByColumnReset : function() {
        this.cellsByColumn = {
          index : 0
        };
        // get this.cellsByColumn.columnWidth
        this._getSegments();
        // get this.cellsByColumn.rowHeight
        this._getSegments(true);
      },
  
      _cellsByColumnLayout : function( $elems ) {
        var instance = this,
            props = this.cellsByColumn;
        $elems.each( function(){
          var $this = $(this),
              col = Math.floor( props.index / props.rows ),
              row = props.index % props.rows,
              x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2,
              y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2;
          instance._pushPosition( $this, x, y );
          props.index ++;
        });
      },
  
      _cellsByColumnGetContainerSize : function() {
        return { width : Math.ceil( this.$filteredAtoms.length / this.cellsByColumn.rows ) * this.cellsByColumn.columnWidth };
      },
  
      _cellsByColumnResizeChanged : function() {
        return this._checkIfSegmentsChanged(true);
      },
  
      // ====================== straightAcross ======================
  
      _straightAcrossReset : function() {
        this.straightAcross = {
          x : 0
        };
      },
  
      _straightAcrossLayout : function( $elems ) {
        var instance = this;
        $elems.each( function( i ){
          var $this = $(this);
          instance._pushPosition( $this, instance.straightAcross.x, 0 );
          instance.straightAcross.x += $this.outerWidth(true);
        });
      },
  
      _straightAcrossGetContainerSize : function() {
        return { width : this.straightAcross.x };
      },
  
      _straightAcrossResizeChanged : function() {
        return true;
      }
  
    };
  
  
    // ======================= imagesLoaded Plugin ===============================
    /*!
     * jQuery imagesLoaded plugin v1.1.0
     * http://github.com/desandro/imagesloaded
     *
     * MIT License. by Paul Irish et al.
     */
  
  
    // $('#my-container').imagesLoaded(myFunction)
    // or
    // $('img').imagesLoaded(myFunction)
  
    // execute a callback when all images have loaded.
    // needed because .load() doesn't work on cached images
  
    // callback function gets image collection as argument
    //  `this` is the container
  
    $.fn.imagesLoaded = function( callback ) {
      var $this = this,
          $images = $this.find('img').add( $this.filter('img') ),
          len = $images.length,
          blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
          loaded = [];
  
      function triggerCallback() {
        callback.call( $this, $images );
      }
  
      function imgLoaded( event ) {
        var img = event.target;
        if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
          loaded.push( img );
          if ( --len <= 0 ){
            setTimeout( triggerCallback );
            $images.unbind( '.imagesLoaded', imgLoaded );
          }
        }
      }
  
      // if no images, trigger immediately
      if ( !len ) {
        triggerCallback();
      }
  
      $images.bind( 'load.imagesLoaded error.imagesLoaded',  imgLoaded ).each( function() {
        // cached images don't fire load sometimes, so we reset src.
        var src = this.src;
        // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
        // data uri bypasses webkit log warning (thx doug jones)
        this.src = blank;
        this.src = src;
      });
  
      return $this;
    };
  
  
    // helper function for logging errors
    // $.error breaks jQuery chaining
    var logError = function( message ) {
      if ( window.console ) {
        window.console.error( message );
      }
    };
  
    // =======================  Plugin bridge  ===============================
    // leverages data method to either create or return $.Isotope constructor
    // A bit from jQuery UI
    //   https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
    // A bit from jcarousel
    //   https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
  
    $.fn.isotope = function( options, callback ) {
      if ( typeof options === 'string' ) {
        // call method
        var args = Array.prototype.slice.call( arguments, 1 );
  
        this.each(function(){
          var instance = $.data( this, 'isotope' );
          if ( !instance ) {
            logError( "cannot call methods on isotope prior to initialization; " +
                "attempted to call method '" + options + "'" );
            return;
          }
          if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
            logError( "no such method '" + options + "' for isotope instance" );
            return;
          }
          // apply method
          instance[ options ].apply( instance, args );
        });
      } else {
        this.each(function() {
          var instance = $.data( this, 'isotope' );
          if ( instance ) {
            // apply options & init
            instance.option( options );
            instance._init( callback );
          } else {
            // initialize new instance
            $.data( this, 'isotope', new $.Isotope( options, this, callback ) );
          }
        });
      }
      // return jQuery object
      // so plugin methods do not have to
      return this;
    };
  
  })( window, jQuery );