// Sticky

/* Smart Resize  */
( function ( $, sr ) {
	'use strict';

	// debouncing function from John Hann
	// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
	var debounce = function ( func, threshold, execAsap ) {
		var timeout;

		return function debounced() {
			var obj = this, args = arguments;
			function delayed() {
				if ( !execAsap )
					func.apply( obj, args );
				timeout = null;
			}

			if ( timeout )
				clearTimeout( timeout );
			else if ( execAsap )
				func.apply( obj, args );

			timeout = setTimeout( delayed, threshold || 100 );
		};
	};
	// smartresize 
	jQuery.fn[ sr ] = function ( fn ) { return fn ? this.bind( 'resize', debounce( fn ) ) : this.trigger( sr ); };

} )( jQuery, 'smartresize' );

( function ( $ ) {
	'use strict';

	// jQuery Pin plugin
	$.fn.themePin = function ( options ) {
		var scrollY = 0, lastScrollY = 0, elements = [], disabled = false, $window = $( window ), fixedSideTop = [], fixedSideBottom = [], prevDataTo = [];

		options = options || {};

		var recalculateLimits = function () {
			for ( var i = 0, len = elements.length; i < len; i++ ) {
				var $this = elements[ i ];

				if ( options.minWidth && $window.width() <= options.minWidth ) {
					if ( $this.parent().is( ".pin-wrapper" ) ) { $this.unwrap(); }
					$this.css( { width: "", left: "", top: "", position: "" } );
					disabled = true;
					continue;
				} else {
					disabled = false;
				}

				var $container = options.containerSelector ? ( $this.closest( options.containerSelector ).length ? $this.closest( options.containerSelector ) : $( options.containerSelector ) ) : $( document.body );
				var offset = $this.offset();
				var containerOffset = $container.offset();

				if ( typeof containerOffset == 'undefined' ) {
					continue;
				}

				var parentOffset = $this.parent().offset();

				if ( !$this.parent().is( ".pin-wrapper" ) ) {
					$this.wrap( "<div class='pin-wrapper'>" );
				}

				var pad = $.extend( {
					top: 0,
					bottom: 0
				}, options.padding || {} );

				var pt = parseInt( $this.parent().parent().css( 'padding-top' ) ), pb = parseInt( $this.parent().parent().css( 'padding-bottom' ) );

				if ( typeof options.paddingOffsetTop != 'undefined' ) {
					pad.top += parseInt( options.paddingOffsetTop, 10 );
				} else {
					pad.top += 18;
				}
				if ( typeof options.paddingOffsetBottom != 'undefined' ) {
					pad.bottom = parseInt( options.paddingOffsetBottom, 10 );
				} else {
					pad.bottom = 0;
				}

				var bb = $this.css( 'border-bottom' ), h = $this.outerHeight();
				$this.css( 'border-bottom', '1px solid transparent' );
				var o_h = $this.outerHeight() - h - 1;
				$this.css( 'border-bottom', bb );
				$this.css( { width: $this.outerWidth() <= $this.parent().width() ? $this.outerWidth() : $this.parent().width() } );
				$this.parent().css( "height", $this.outerHeight() + o_h );

				if ( $this.outerHeight() <= $window.height() ) {
					$this.data( "themePin", {
						pad: pad,
						from: ( options.containerSelector ? containerOffset.top : offset.top ) - pad.top + pt,
						pb: pb,
						parentTop: parentOffset.top - pt,
						offset: o_h
					} );
				} else {
					$this.data( "themePin", {
						pad: pad,
						fromFitTop: ( options.containerSelector ? containerOffset.top : offset.top ) - pad.top + pt,
						from: ( options.containerSelector ? containerOffset.top : offset.top ) + $this.outerHeight() - $window.height() + pt,
						pb: pb,
						parentTop: parentOffset.top - pt,
						offset: o_h
					} );
				}
			}
		};

		var onScroll = function () {
			if ( disabled ) { return; }

			scrollY = $window.scrollTop();

			var window_height = window.innerHeight || $window.height();

			for ( var i = 0, len = elements.length; i < len; i++ ) {
				var $this = $( elements[ i ] ),
					data = $this.data( "themePin" ),
					sidebarTop;

				if ( !data ) { // Removed element
					continue;
				}

				var $container = options.containerSelector ? ( $this.closest( options.containerSelector ).length ? $this.closest( options.containerSelector ) : $( options.containerSelector ) ) : $( document.body ),
					this_height = $this.outerHeight(),
					isFitToTop = ( this_height + data.pad.top ) <= window_height;

				data.end = $container.offset().top + $container.height();
				if ( isFitToTop ) {
					data.to = $container.offset().top + $container.height() - this_height - data.pad.bottom - data.pb;
				} else {
					data.to = $container.offset().top + $container.height() - window_height - data.pb;
					data.to2 = $container.height() - this_height - data.pad.bottom - data.pb;
				}

				if ( prevDataTo[ i ] === 0 ) {
					prevDataTo[ i ] = data.to;
				}

				if ( prevDataTo[ i ] != data.to ) {
					if ( fixedSideBottom[ i ] && this_height + $this.offset().top + data.pad.bottom < scrollY + window_height ) {
						fixedSideBottom[ i ] = false;
					}
				}

				if ( isFitToTop ) {
					var from = data.from - data.pad.bottom,
						to = data.to - data.pad.top - data.offset;
					if ( typeof data.fromFitTop != 'undefined' && data.fromFitTop ) {
						from = data.fromFitTop - data.pad.bottom;
					}

					if ( from + this_height > data.end || from >= to ) {
						$this.css( { position: "", top: "", left: "" } );
						if ( options.activeClass ) { $this.removeClass( options.activeClass ); }
						continue;
					}
					if ( scrollY > from && scrollY < to ) {
						!( $this.css( "position" ) == "fixed" ) && $this.css( {
							left: $this.offset().left,
							top: data.pad.top
						} ).css( "position", "fixed" );
						if ( options.activeClass ) { $this.addClass( options.activeClass ); }
					} else if ( scrollY >= to ) {
						$this.css( {
							left: "",
							top: to - data.parentTop + data.pad.top
						} ).css( "position", "absolute" );
						if ( options.activeClass ) { $this.addClass( options.activeClass ); }
					} else {
						$this.css( { position: "", top: "", left: "" } );
						if ( options.activeClass ) { $this.removeClass( options.activeClass ); }
					}
				} else if ( ( this_height + data.pad.top + data.pad.bottom ) > window_height || fixedSideTop[ i ] || fixedSideBottom[ i ] ) {
					var padTop = parseInt( $this.parent().parent().css( 'padding-top' ) );
					// Reset the sideSortables style when scrolling to the top.
					if ( scrollY + data.pad.top - padTop <= data.parentTop ) {
						$this.css( { position: "", top: "", bottom: "", left: "" } );
						fixedSideTop[ i ] = fixedSideBottom[ i ] = false;

						if ( options.activeClass ) { $this.removeClass( options.activeClass ); }
					} else if ( scrollY >= data.to ) {
						$this.css( {
							left: "",
							top: data.to2,
							bottom: ""
						} ).css( "position", "absolute" );
						if ( options.activeClass ) { $this.addClass( options.activeClass ); }
					} else {

						// When scrolling down.
						if ( scrollY > lastScrollY ) {
							if ( fixedSideTop[ i ] ) {

								// Let it scroll.
								fixedSideTop[ i ] = false;
								sidebarTop = $this.offset().top - data.parentTop;

								$this.css( {
									left: "",
									top: sidebarTop,
									bottom: ""
								} ).css( "position", "absolute" );
								if ( options.activeClass ) { $this.addClass( options.activeClass ); }
							} else if ( !fixedSideBottom[ i ] && this_height + $this.offset().top + data.pad.bottom < scrollY + window_height ) {
								// Pin the bottom.
								fixedSideBottom[ i ] = true;

								!( $this.css( "position" ) == "fixed" ) && $this.css( {
									left: $this.offset().left,
									bottom: data.pad.bottom,
									top: ""
								} ).css( "position", "fixed" );
								if ( options.activeClass ) { $this.addClass( options.activeClass ); }
							}

							// When scrolling up.
						} else if ( scrollY < lastScrollY ) {
							if ( fixedSideBottom[ i ] ) {
								// Let it scroll.
								fixedSideBottom[ i ] = false;
								sidebarTop = $this.offset().top - data.parentTop;

								$this.css( {
									left: "",
									top: sidebarTop,
									bottom: ""
								} ).css( "position", "absolute" );
								if ( options.activeClass ) { $this.addClass( options.activeClass ); }
							} else if ( !fixedSideTop[ i ] && $this.offset().top >= scrollY + data.pad.top ) {
								// Pin the top.
								fixedSideTop[ i ] = true;

								!( $this.css( "position" ) == "fixed" ) && $this.css( {
									left: $this.offset().left,
									top: data.pad.top,
									bottom: ''
								} ).css( "position", "fixed" );
								if ( options.activeClass ) { $this.addClass( options.activeClass ); }
							}
						} else {
							fixedSideTop[ i ] = false;
							fixedSideTop[ i ] = false;
						}
					}
				} else {
					// If the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling.
					if ( scrollY >= ( data.parentTop - data.pad.top ) ) {
						$this.css( {
							position: 'fixed',
							top: data.pad.top
						} );
					} else {
						$this.css( { position: "", top: "", bottom: "", left: "" } );
					}

					fixedSideTop[ i ] = fixedSideBottom[ i ] = false;
				}

				prevDataTo[ i ] = data.to;
			}

			lastScrollY = scrollY;
		};

		var recalculateLeft = function ( timeout ) {
			if ( typeof timeout == 'undefined' ) {
				timeout = 400;
			}
			for ( var i = 0, len = elements.length; i < len; i++ ) {
				var $this = $( elements[ i ] ),
					data = $this.data( "themePin" );

				if ( !data ) { // Removed element
					continue;
				}

				var $container = options.containerSelector ? ( $this.closest( options.containerSelector ).length ? $this.closest( options.containerSelector ) : $( options.containerSelector ) ) : $( document.body );

				if ( $this.css( "position" ) == "fixed" ) {
					var offset = $this.offset().top - $container.offset().top;
					$this.css( {
						'position': 'absolute',
						left: '',
						top: offset,
						bottom: ''
					} );
				}
			}
		};

		var update = function () { recalculateLimits(); onScroll(); };

		this.each( function () {
			var $this = $( this ),
				data = $( this ).data( 'themePin' ) || {};

			if ( data && data.update ) { return; }
			elements.push( $this );
			$( "img", this ).one( "load", recalculateLimits );
			data.update = update;
			$( this ).data( 'themePin', data );
			fixedSideTop.push( false );
			fixedSideBottom.push( false );
			prevDataTo.push( 0 );
		} );

		$window.on( 'touchmove scroll', onScroll );
		recalculateLimits();

		$window.on( 'load', update );

		$( this ).on( 'recalc.pin', function () {
			recalculateLimits();
			onScroll();
		} );

		$( this ).on( 'recalc.pin.left', function ( e, timeout ) {
			recalculateLeft( timeout );
		} );

		return this;
	};

	var instanceName = '__sticky';

	var Sticky = function ( $el, opts ) {
		return this.initialize( $el, opts );
	};

	Sticky.defaults = {
		autoInit: false,
		minWidth: 767,
		padding: {
			top: 0,
			bottom: 0
		},
		offsetTop: 0,
		offsetBottom: 0
	};

	Sticky.prototype = {
		initialize: function ( $el, opts ) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions( opts )
				.build();

			return this;
		},

		setData: function () {
			this.$el.data( instanceName, this );

			return this;
		},

		setOptions: function ( opts ) {
			this.options = $.extend( true, {}, Sticky.defaults, opts, {
				wrapper: this.$el
			} );

			return this;
		},

		build: function () {
			if ( !( $.isFunction( $.fn.themePin ) ) ) {
				return this;
			}

			var self = this,
				$el = this.options.wrapper,
				stickyResizeTrigger;

			$el.themePin( this.options );

			$( window ).smartresize( function () {
				if ( stickyResizeTrigger ) {
					clearTimeout( stickyResizeTrigger );
				}
				stickyResizeTrigger = setTimeout( function () {
					$el.trigger( 'recalc.pin' );
				}, 800 );

				var $parent = $el.parent();

				$el.outerWidth( $parent.width() );
				if ( $el.css( 'position' ) == 'fixed' ) {
					$el.css( 'left', $parent.offset().left );
				}
			} );

			return this;
		}
	};

	// jquery plugin
	$.fn.themeSticky = function ( opts ) {
		return this.map( function () {
			var $this = $( this );
			if ( $this.data( instanceName ) ) {
				$this.trigger( 'recalc.pin' );
				setTimeout( function () {
					$this.trigger( 'recalc.pin' );
				}, 800 );
				return $this.data( instanceName );
			} else {
				return new Sticky( $this, opts );
			}
		} );
	}
} ).apply( this, [ jQuery ] );