// 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( "
" );
}
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 ] );