/*************************************************
**  jQuery Masonry version 1.3.2
**  Copyright David DeSandro, licensed MIT
**  http://desandro.com/resources/jquery-masonry
**************************************************/
;(function($){  

  /*!
   * smartresize: debounced resize event for jQuery
   * http://github.com/lrbabe/jquery-smartresize
   *
   * Copyright (c) 2009 Louis-Remi Babe
   * Licensed under the GPL license.
   * http://docs.jquery.com/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"] );
  };



  // masonry code begin
  $.fn.masonry = function(options, callback) { 

    // all my sweet methods
    var msnry = {
      getBricks : function($wall, props, opts) {
        var hasItemSelector = (opts.itemSelector === undefined);
        if ( opts.appendedContent === undefined ) {
          // if not appendedContent
          props.$bricks = hasItemSelector ?
                $wall.children() :
                $wall.find(opts.itemSelector);
        } else {
         //  if appendedContent...
         props.$bricks = hasItemSelector ?
                opts.appendedContent : 
                opts.appendedContent.filter( opts.itemSelector );
        }
      },
      
      placeBrick : function($brick, setCount, setY, props, opts) {
            // get the minimum Y value from the columns...
        var minimumY = Math.min.apply(Math, setY),
            setHeight = minimumY + $brick.outerHeight(true),
            i = setY.length,
            shortCol = i,
            setSpan = props.colCount + 1 - i;
        // Which column has the minY value, closest to the left
        while (i--) {
          if ( setY[i] == minimumY ) {
            shortCol = i;
          }
        }
            
        var position = {
          left: props.colW * shortCol + props.posLeft,
          top: minimumY
        };
            
        // position the brick
        $brick.applyStyle(position, $.extend(true,{},opts.animationOptions) );

        // apply setHeight to necessary columns
        for ( i=0; i < setSpan; i++ ) {
          props.colY[ shortCol + i ] = setHeight;
        }
      },
      
      setup : function($wall, opts, props) {
        msnry.getBricks($wall, props, opts);

        if ( props.masoned ) {
          props.previousData = $wall.data('masonry');
        }

        if ( opts.columnWidth === undefined) {
          props.colW = props.masoned ?
              props.previousData.colW :
              props.$bricks.outerWidth(true);
        } else {
          props.colW = opts.columnWidth;
        }

        props.colCount = Math.floor( $wall.width() / props.colW ) ;
        props.colCount = Math.max( props.colCount, 1 );
      },
      
      arrange : function($wall, opts, props) {
        var i;

        if ( !props.masoned || opts.appendedContent !== undefined ) {
          // just the new bricks
          props.$bricks.css( 'position', 'absolute' );
        }

        // if masonry hasn't been called before
        if ( !props.masoned ) { 
          $wall.css( 'position', 'relative' );

          // get top left position of where the bricks should be
          var $cursor = $( document.createElement('div') );
          $wall.prepend( $cursor );
          props.posTop =  Math.round( $cursor.position().top );
          props.posLeft = Math.round( $cursor.position().left );
          $cursor.remove();
        } else {
          props.posTop =  props.previousData.posTop;
          props.posLeft = props.previousData.posLeft;
        }
        
        // set up column Y array
        if ( props.masoned && opts.appendedContent !== undefined ) {
          // if appendedContent is set, use colY from last call
          props.colY = props.previousData.colY;

          /*
          *  in the case that the wall is not resizeable,
          *  but the colCount has changed from the previous time
          *  masonry has been called
          */
          for ( i = props.previousData.colCount; i < props.colCount; i++) {
            props.colY[i] = props.posTop;
          }

        } else {
          // start new colY array, with starting values set to posTop
          props.colY = [];
          i = props.colCount;
          while (i--) {
            props.colY.push(props.posTop);
          }
        }

        // are we animating the rearrangement?
        // use plugin-ish syntax for css or animate
        $.fn.applyStyle = ( props.masoned && opts.animate ) ? $.fn.animate : $.fn.css;


        // layout logic
        if ( opts.singleMode ) {
          props.$bricks.each(function(){
            var $brick = $(this);
            msnry.placeBrick($brick, props.colCount, props.colY, props, opts);
          });      
        } else {
          props.$bricks.each(function() {
            var $brick = $(this),
                //how many columns does this brick span
                colSpan = Math.ceil( $brick.outerWidth(true) / props.colW);
            colSpan = Math.min( colSpan, props.colCount );

            if ( colSpan === 1 ) {
              // if brick spans only one column, just like singleMode
              msnry.placeBrick($brick, props.colCount, props.colY, props, opts);
            } else {
              // brick spans more than one column

              //how many different places could this brick fit horizontally
              var groupCount = props.colCount + 1 - colSpan,
                  groupY = [];

              // for each group potential horizontal position
              for ( i=0; i < groupCount; i++ ) {
                // make an array of colY values for that one group
                var groupColY = props.colY.slice(i, i+colSpan);
                // and get the max value of the array
                groupY[i] = Math.max.apply(Math, groupColY);
              }

              msnry.placeBrick($brick, groupCount, groupY, props, opts);
            }
          }); //    /props.bricks.each(function() {
        }  //     /layout logic

        // set the height of the wall to the tallest column
        props.wallH = Math.max.apply(Math, props.colY);
        var wallCSS = { height: props.wallH - props.posTop };
        $wall.applyStyle( wallCSS, $.extend(true,[],opts.animationOptions) );

        // add masoned class first time around
        if ( !props.masoned ) { 
          // wait 1 millisec for quell transitions
          setTimeout(function(){
            $wall.addClass('masoned'); 
          }, 1);
        }

        // provide props.bricks as context for the callback
        callback.call( props.$bricks );

        // set all data so we can retrieve it for appended appendedContent
        //    or anyone else's crazy jquery fun
        $wall.data('masonry', props );
        
      }, // /msnry.arrange
      
      resize : function($wall, opts, props) {
        props.masoned = !!$wall.data('masonry');
        var prevColCount = $wall.data('masonry').colCount;
        msnry.setup($wall, opts, props);
        if ( props.colCount != prevColCount ) {
          msnry.arrange($wall, opts, props);
        }
      }
    };


    /*
    *  let's begin
    *  IN A WORLD...
    */
    return this.each(function() {  

      var $wall = $(this),
          props = {};

      // checks if masonry has been called before on this object
      props.masoned = !!$wall.data('masonry');
    
      var previousOptions = props.masoned ? $wall.data('masonry').options : {},
          opts =  $.extend(
                    {},
                    $.fn.masonry.defaults,
                    previousOptions,
                    options
                  ),
          resizeOn = previousOptions.resizeable;

      // should we save these options for next time?
      props.options = opts.saveOptions ? opts : previousOptions;

      //picked up from Paul Irish
      callback = callback || function(){};

      msnry.getBricks($wall, props, opts);

      // if brickParent is empty, do nothing, go back home and eat chips
      if ( !props.$bricks.length ) { 
        return this; 
      }

      // call masonry layout
      msnry.setup($wall, opts, props);
      msnry.arrange($wall, opts, props);
    
      // binding window resizing
      if ( !resizeOn && opts.resizeable ) {
        $(window).bind('smartresize.masonry', function() { msnry.resize($wall, opts, props); } );
      }
      if ( resizeOn && !opts.resizeable ) { 
        $(window).unbind('smartresize.masonry'); 
      }
       

    });    //    /return this.each(function()
  };      //    /$.fn.masonry = function(options)


  // Default plugin options
  $.fn.masonry.defaults = {
    singleMode: false,
    columnWidth: undefined,
    itemSelector: undefined,
    appendedContent: undefined,
    saveOptions: true,
    resizeable: true,
    animate: false,
    animationOptions: {}
  };

})(jQuery);