ssl.neutralnetwork.org/assets/js/jquery/jquery.smooth-scroll.js
2015-03-02 21:01:53 +01:00

249 lines
7 KiB
JavaScript

(function($) {
var version = '1.4.13',
optionOverrides = {},
defaults = {
exclude: [],
excludeWithin:[],
offset: 0,
// one of 'top' or 'left'
direction: 'top',
// jQuery set of elements you wish to scroll (for $.smoothScroll).
// if null (default), $('html, body').firstScrollable() is used.
scrollElement: null,
// only use if you want to override default behavior
scrollTarget: null,
// fn(opts) function to be called before scrolling occurs.
// `this` is the element(s) being scrolled
beforeScroll: function() {},
// fn(opts) function to be called after scrolling occurs.
// `this` is the triggering element
afterScroll: function() {},
easing: 'swing',
speed: 400,
// coefficient for "auto" speed
autoCoefficent: 2,
// $.fn.smoothScroll only: whether to prevent the default click action
preventDefault: true
},
getScrollable = function(opts) {
var scrollable = [],
scrolled = false,
dir = opts.dir && opts.dir == 'left' ? 'scrollLeft' : 'scrollTop';
this.each(function() {
if (this == document || this == window) { return; }
var el = $(this);
if ( el[dir]() > 0 ) {
scrollable.push(this);
} else {
// if scroll(Top|Left) === 0, nudge the element 1px and see if it moves
el[dir](1);
scrolled = el[dir]() > 0;
if ( scrolled ) {
scrollable.push(this);
}
// then put it back, of course
el[dir](0);
}
});
// If no scrollable elements, fall back to <body>,
// if it's in the jQuery collection
// (doing this because Safari sets scrollTop async,
// so can't set it to 1 and immediately get the value.)
if (!scrollable.length) {
this.each(function(index) {
if (this.nodeName === 'BODY') {
scrollable = [this];
}
});
}
// Use the first scrollable element if we're calling firstScrollable()
if ( opts.el === 'first' && scrollable.length > 1 ) {
scrollable = [ scrollable[0] ];
}
return scrollable;
},
isTouch = 'ontouchend' in document;
$.fn.extend({
scrollable: function(dir) {
var scrl = getScrollable.call(this, {dir: dir});
return this.pushStack(scrl);
},
firstScrollable: function(dir) {
var scrl = getScrollable.call(this, {el: 'first', dir: dir});
return this.pushStack(scrl);
},
smoothScroll: function(options, extra) {
options = options || {};
if ( options === 'options' ) {
if ( !extra ) {
return this.first().data('ssOpts');
}
return this.each(function() {
var $this = $(this),
opts = $.extend($this.data('ssOpts') || {}, extra);
$(this).data('ssOpts', opts);
});
}
var opts = $.extend({}, $.fn.smoothScroll.defaults, options),
locationPath = $.smoothScroll.filterPath(location.pathname);
this
.unbind('click.smoothscroll')
.bind('click.smoothscroll', function(event) {
var link = this,
$link = $(this),
thisOpts = $.extend({}, opts, $link.data('ssOpts') || {}),
exclude = opts.exclude,
excludeWithin = thisOpts.excludeWithin,
elCounter = 0, ewlCounter = 0,
include = true,
clickOpts = {},
hostMatch = ((location.hostname === link.hostname) || !link.hostname),
pathMatch = thisOpts.scrollTarget || ( $.smoothScroll.filterPath(link.pathname) || locationPath ) === locationPath,
thisHash = escapeSelector(link.hash);
if ( !thisOpts.scrollTarget && (!hostMatch || !pathMatch || !thisHash) ) {
include = false;
} else {
while (include && elCounter < exclude.length) {
if ($link.is(escapeSelector(exclude[elCounter++]))) {
include = false;
}
}
while ( include && ewlCounter < excludeWithin.length ) {
if ($link.closest(excludeWithin[ewlCounter++]).length) {
include = false;
}
}
}
if ( include ) {
if ( thisOpts.preventDefault ) {
event.preventDefault();
}
$.extend( clickOpts, thisOpts, {
scrollTarget: thisOpts.scrollTarget || thisHash,
link: link
});
$.smoothScroll( clickOpts );
}
});
return this;
}
});
$.smoothScroll = function(options, px) {
if ( options === 'options' && typeof px === 'object' ) {
return $.extend(optionOverrides, px);
}
var opts, $scroller, scrollTargetOffset, speed,
scrollerOffset = 0,
offPos = 'offset',
scrollDir = 'scrollTop',
aniProps = {},
aniOpts = {},
scrollprops = [];
if (typeof options === 'number') {
opts = $.extend({link: null}, $.fn.smoothScroll.defaults, optionOverrides);
scrollTargetOffset = options;
} else {
opts = $.extend({link: null}, $.fn.smoothScroll.defaults, options || {}, optionOverrides);
if (opts.scrollElement) {
offPos = 'position';
if (opts.scrollElement.css('position') == 'static') {
opts.scrollElement.css('position', 'relative');
}
}
}
scrollDir = opts.direction == 'left' ? 'scrollLeft' : scrollDir;
if ( opts.scrollElement ) {
$scroller = opts.scrollElement;
if ( !(/^(?:HTML|BODY)$/).test($scroller[0].nodeName) ) {
scrollerOffset = $scroller[scrollDir]();
}
} else {
$scroller = $('html, body').firstScrollable(opts.direction);
}
// beforeScroll callback function must fire before calculating offset
opts.beforeScroll.call($scroller, opts);
scrollTargetOffset = (typeof options === 'number') ? options :
px ||
( $(opts.scrollTarget)[offPos]() &&
$(opts.scrollTarget)[offPos]()[opts.direction] ) ||
0;
aniProps[scrollDir] = scrollTargetOffset + scrollerOffset + opts.offset;
speed = opts.speed;
// automatically calculate the speed of the scroll based on distance / coefficient
if (speed === 'auto') {
// if aniProps[scrollDir] == 0 then we'll use scrollTop() value instead
speed = aniProps[scrollDir] || $scroller.scrollTop();
// divide the speed by the coefficient
speed = speed / opts.autoCoefficent;
}
aniOpts = {
duration: speed,
easing: opts.easing,
complete: function() {
opts.afterScroll.call(opts.link, opts);
}
};
if (opts.step) {
aniOpts.step = opts.step;
}
if ($scroller.length) {
$scroller.stop().animate(aniProps, aniOpts);
} else {
opts.afterScroll.call(opts.link, opts);
}
};
$.smoothScroll.version = version;
$.smoothScroll.filterPath = function(string) {
return string
.replace(/^\//,'')
.replace(/(?:index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
};
// default options
$.fn.smoothScroll.defaults = defaults;
function escapeSelector (str) {
return str.replace(/(:|\.)/g,'\\$1');
}
})(jQuery);