// github.com/filamentgroup/fixed-sticky ;(function( win, $ ) {

function featureTest( property, value, noPrefixes ) {
  // Thanks Modernizr! https://github.com/phistuck/Modernizr/commit/3fb7217f5f8274e2f11fe6cfeda7cfaf9948a1f5
  var prop = property + ':',
    el = document.createElement( 'test' ),
    mStyle = el.style;

  if( !noPrefixes ) {
    mStyle.cssText = prop + [ '-webkit-', '-moz-', '-ms-', '-o-', '' ].join( value + ';' + prop ) + value + ';';
  } else {
    mStyle.cssText = prop + value;
  }
  return mStyle[ property ].indexOf( value ) !== -1;
}

function getPx( unit ) {
  return parseInt( unit, 10 ) || 0;
}

var uniqueIdCounter = 0;

var S = {
  classes: {
    plugin: 'fixedsticky',
    active: 'fixedsticky-on',
    inactive: 'fixedsticky-off',
    clone: 'fixedsticky-dummy',
    withoutFixedFixed: 'fixedsticky-withoutfixedfixed'
  },
  keys: {
    offset: 'fixedStickyOffset',
    position: 'fixedStickyPosition',
    id: 'fixedStickyId'
  },
  tests: {
    sticky: featureTest( 'position', 'sticky' ),
    fixed: featureTest( 'position', 'fixed', true )
  },
  // Thanks jQuery!
  getScrollTop: function() {
    var prop = 'pageYOffset',
      method = 'scrollTop';
    return win ? (prop in win) ? win[ prop ] :
      win.document.documentElement[ method ] :
      win.document.body[ method ];
  },
  bypass: function() {
    // Check native sticky, check fixed and if fixed-fixed is also included on the page and is supported
    return ( S.tests.sticky && !S.optOut ) ||
      !S.tests.fixed ||
      win.FixedFixed && !$( win.document.documentElement ).hasClass( 'fixed-supported' );
  },
  update: function( el ) {
    if( !el.offsetWidth ) { return; }

    var $el = $( el ),
      height = $el.outerHeight(),
      initialOffset = $el.data( S.keys.offset ),
      scroll = S.getScrollTop(),
      isAlreadyOn = $el.is( '.' + S.classes.active ),
      toggle = function( turnOn ) {
        $el[ turnOn ? 'addClass' : 'removeClass' ]( S.classes.active )
          [ !turnOn ? 'addClass' : 'removeClass' ]( S.classes.inactive );
      },
      viewportHeight = $( window ).height(),
      position = $el.data( S.keys.position ),
      skipSettingToFixed,
      elTop,
      elBottom,
      $parent = $el.parent(),
      parentOffset = $parent.offset().top,
      parentHeight = $parent.outerHeight();

    if( initialOffset === undefined ) {
      initialOffset = $el.offset().top;
      $el.data( S.keys.offset, initialOffset );
      $el.after( $( '<div>' ).addClass( S.classes.clone ).height( height ) );
    } else {
      $el.next( '.' + S.classes.clone ).height( height );
    }

    if( !position ) {
      // Some browsers require fixed/absolute to report accurate top/left values.
      skipSettingToFixed = $el.css( 'top' ) !== 'auto' || $el.css( 'bottom' ) !== 'auto';

      if( !skipSettingToFixed ) {
        $el.css( 'position', 'fixed' );
      }

      position = {
        top: $el.css( 'top' ) !== 'auto',
        bottom: $el.css( 'bottom' ) !== 'auto'
      };

      if( !skipSettingToFixed ) {
        $el.css( 'position', '' );
      }

      $el.data( S.keys.position, position );
    }

    function isFixedToTop() {
      var offsetTop = scroll + elTop;

      // Initial Offset Top
      return initialOffset < offsetTop &&
        // Container Bottom
        offsetTop + height <= parentOffset + parentHeight;
    }

    function isFixedToBottom() {
      // Initial Offset Top + Height
      return initialOffset + ( height || 0 ) > scroll + viewportHeight - elBottom &&
        // Container Top
        scroll + viewportHeight - elBottom >= parentOffset + ( height || 0 );
    }

    elTop = getPx( $el.css( 'top' ) );
    elBottom = getPx( $el.css( 'bottom' ) );

    if( position.top && isFixedToTop() || position.bottom && isFixedToBottom() ) {
      if( !isAlreadyOn ) {
        toggle( true );
      }
    } else {
      if( isAlreadyOn ) {
        toggle( false );
      }
    }
  },
  destroy: function( el ) {
    var $el = $( el );
    if (S.bypass()) {
      return $el;
    }

    return $el.each(function() {
      var $this = $( this );
      var id = $this.data( S.keys.id );
      $( win ).unbind( '.fixedsticky' + id );

      $this
        .removeData( [ S.keys.offset, S.keys.position, S.keys.id ] )
        .removeClass( S.classes.active )
        .removeClass( S.classes.inactive )
        .next( '.' + S.classes.clone ).remove();
    });
  },
  init: function( el ) {
    var $el = $( el );

    if( S.bypass() ) {
      return $el;
    }

    return $el.each(function() {
      var _this = this;
      var id = uniqueIdCounter++;
      $( this ).data( S.keys.id, id );

      $( win ).bind( 'scroll.fixedsticky' + id, function() {
        S.update( _this );
      }).trigger( 'scroll.fixedsticky' + id );

      $( win ).bind( 'resize.fixedsticky' + id , function() {
        if( $el.is( '.' + S.classes.active ) ) {
          S.update( _this );
        }
      });
    });
  }
};

win.FixedSticky = S;

// Plugin
$.fn.fixedsticky = function( method ) {
  if ( typeof S[ method ] === 'function') {
    return S[ method ].call( S, this);
  } else if ( typeof method === 'object' || ! method ) {
    return S.init.call( S, this );
  } else {
    throw new Error( 'Method `' +  method + '` does not exist on jQuery.fixedsticky' );
  }
};

// Add fallback when fixed-fixed is not available.
if( !win.FixedFixed ) {
  $( win.document.documentElement ).addClass( S.classes.withoutFixedFixed );
}

})( window, jQuery );