/*@preserve * Copyright (c) 2013 Ben Olson (https://github.com/bseth99/jquery-ui-scrollable) * jQuery UI Scrollable 0.1.1 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Depends: * jquery.ui.core.js * jquery.ui.widget.js */ (function ($) { "use strict"; var rootex = /^(?:html)$/i, max = Math.max, abs = Math.abs, round = Math.round, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/; /** * Helpers from jQuery UI Position * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } /* * End jQuery UI Position **/ /** * throttle from: * http://remysharp.com/2010/07/21/throttling-function-calls/ */ function throttle(fn, threshhold, scope) { threshhold || (threshhold = 250); var last, deferTimer; return function () { var context = scope || this; var now = +new Date, args = arguments; if (last && now < last + threshhold) { // hold on to it clearTimeout(deferTimer); deferTimer = setTimeout(function () { last = now; fn.apply(context, args); }, threshhold); } else { last = now; fn.apply(context, args); } }; } function trackScrolling( scroller ) { var _waiter = null; scroller.onscroll = throttle( function ( e ) { if ( !scroller.ignoreScrolling ) { $.each(scroller.watch, function () { this._checkPositioning( e ); }); } }, $.osb.scrollable.CONFIG.throttler ); //console.log( 'Start listening to scrolling' ); scroller.element.on('scroll.scrollable', scroller.onscroll); } var monitor = { scrollers: [], getScroller: function ( obj ) { var scroller; for ( var i=0;i doc.right, top: elem.bottom < doc.top, bottom: elem.top > doc.bottom }; ret.inside = !ret.left && !ret.right && !ret.top && !ret.bottom; ret.outside = !ret.inside; //console.log( ret ); return ret; }, goto: function ( options ) { var self = this, options = options || {}, offsets = {}, position = this.position(), targetWidth = this.container.width(), targetHeight = this.container.height(), elemWidth = this.element.outerWidth(true), elemHeight = this.element.outerHeight(true), dir = this.options.direction, target = (this.container[0] === window ? $('html') : this.container), scroll, atOffset, myOffset; /** * Adapted from jQuery UI Position * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ if ( !options.onlyOutside || ( options.onlyOutside && !this.inView ) ) { $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; }); if ( options.at[ 0 ] === "right" ) { position.element.left -= targetWidth; } else if ( options.at[ 0 ] === "center" ) { position.element.left -= targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { position.element.top -= targetHeight; } else if ( options.at[ 1 ] === "center" ) { position.element.top -= targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); position.element.left -= atOffset[ 0 ]; position.element.top -= atOffset[ 1 ]; myOffset = getOffsets( offsets.my, elemWidth, elemHeight ); if ( options.my[ 0 ] === "right" ) { position.element.left += elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.element.left += elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.element.top += elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.element.top += elemHeight / 2; } position.element.left -= myOffset[ 0 ]; position.element.top -= myOffset[ 1 ]; /* * End jQuery UI Position **/ scroll = {}; if ( dir == 'both' || dir == 'horizontal' ) scroll.scrollLeft = round(position.element.left)+'px'; if ( dir == 'both' || dir == 'vertical' ) scroll.scrollTop = round(position.element.top)+'px'; monitor.scrollInProgress( this, true ); target .animate( scroll, { duration: options.duration || 'slow', easing: options.easing || 'swing', complete: function() { monitor.scrollInProgress( self, false ); self._checkPositioning(); if ($.isFunction( options.complete ) ) options.complete.call(); } }); } else { if ( $.isFunction( options.complete ) ) options.complete.call( this.element ); } } }); /** * Inspired by (and similar to) https://github.com/litera/jquery-scrollintoview * */ $.expr[":"].scrollable = $.expr.createPseudo(function( dir ) { var dir = (!dir || dir == 'undefined' ? 'both' : dir); return function( elem ) { var $el = $(elem), isRoot = rootex.test(elem.nodeName), styles = $el.css(['overflow-x', 'overflow-y']), overflow = { x: (styles['overflow-x'] == 'auto' || styles['overflow-x'] == 'scroll'), y: (styles['overflow-y'] == 'auto' || styles['overflow-y'] == 'scroll') }, test = false; if ( !isRoot && !overflow.x && !overflow.y ) { return false; } if ( dir == 'both' || dir == 'vertical' ) test = test || ( overflow.x || isRoot ) && elem.scrollWidth > elem.clientWidth; if ( dir == 'both' || dir == 'horizontal' ) test = test || ( overflow.y || isRoot ) && elem.scrollHeight > elem.clientHeight; return test; } }); /** * Fix scrolling animations on html/body * https://github.com/balupton/jquery-scrollto * */ $.propHooks.scrollTop = $.propHooks.scrollLeft = { get: function(elem,prop) { var result = null; if ( elem.tagName === 'HTML' || elem.tagName === 'BODY' ) { if ( prop === 'scrollLeft' ) { result = window.scrollX; } else if ( prop === 'scrollTop' ) { result = window.scrollY; } } if ( result == null ) { result = elem[prop]; } return result; } }; $.Tween.propHooks.scrollTop = $.Tween.propHooks.scrollLeft = { get: function(tween) { return $.propHooks.scrollTop.get(tween.elem, tween.prop); }, set: function(tween) { // Our safari fix if ( tween.elem.tagName === 'HTML' || tween.elem.tagName === 'BODY' ) { // Defaults tween.options.bodyScrollLeft = (tween.options.bodyScrollLeft || window.scrollX); tween.options.bodyScrollTop = (tween.options.bodyScrollTop || window.scrollY); // Apply if ( tween.prop === 'scrollLeft' ) { tween.options.bodyScrollLeft = Math.round(tween.now); } else if ( tween.prop === 'scrollTop' ) { tween.options.bodyScrollTop = Math.round(tween.now); } // Apply window.scrollTo(tween.options.bodyScrollLeft, tween.options.bodyScrollTop); } // jQuery's IE8 Fix else if ( tween.elem.nodeType && tween.elem.parentNode ) { tween.elem[ tween.prop ] = tween.now; } } }; $.osb.scrollable.CONFIG = { throttler: 300 } })(jQuery);