/cody/learning-zone/website-templates/johndoe-portfolio-resume-bootstrap-template/js/jquery.isotope.js
/** * 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() === 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 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 );