/*! * Smooth Scroll - v1.4.13 - 2013-11-02 * https://github.com/kswedberg/jquery-smooth-scroll * Copyright (c) 2013 Karl Swedberg * Licensed MIT (https://github.com/kswedberg/jquery-smooth-scroll/blob/master/LICENSE-MIT) */ (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 , // 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); jQuery.fn.scrollStopped = function(callback) { jQuery(this).scroll(function(){ var self = this, $this = jQuery(self); if ($this.data('scrollTimeout')) { clearTimeout($this.data('scrollTimeout')); } $this.data('scrollTimeout', setTimeout(callback,250,self)); }); }; // findPos : courtesy of @ppk - see http://www.quirksmode.org/js/findpos.html var findPos = function(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft; curtop = obj.offsetTop; while ((obj = obj.offsetParent)) { curleft += obj.offsetLeft; curtop += obj.offsetTop; } } return [curleft, curtop]; }; var findClosestAnchor = function (anchors) { var sortByDistance = function(element1, element2) { var pos1 = findPos( element1 ), pos2 = findPos( element2 ); // vect1 & vect2 represent 2d vectors going from the top left extremity of each element to the point positionned at the scrolled offset of the window var vect1 = [ window.scrollX - pos1[0], window.scrollY - pos1[1] ], vect2 = [ window.scrollX - pos2[0], window.scrollY - pos2[1] ]; // we compare the length of the vectors using only the sum of their components squared // no need to find the magnitude of each (this was inspired by Mageek’s answer) var sqDist1 = vect1[0] * vect1[0] + vect1[1] * vect1[1], sqDist2 = vect2[0] * vect2[0] + vect2[1] * vect2[1]; if ( sqDist1 < sqDist2 ) return -1; else if ( sqDist1 > sqDist2 ) return 1; else return 0; }; // Convert the nodelist to an array, then returns the first item of the elements sorted by distance return Array.prototype.slice.call( anchors ).sort( sortByDistance )[0]; }; function linkVisibility(url) { var url = String(url); var hash = url.substr(url.indexOf("#")); var hideme = '.ahead, .rewind'; var showme = '.ahead, .rewind'; // var hey = ''; // switch (hash){ // case '#prev-chapter': // hey = "previous chapter!"; // showme = '.ahead'; // hideme = ''; // break; // // case '#next-chapter': // showme = '.rewind'; // hideme = ''; // break; // } jQuery(hideme).hide(); jQuery(hash).siblings(showme).show(); // alert (hash); // if (hey != '') { // alert (hey); // } } var lastSmoothScroll = Date.now()-20; var isScrolling = false; // smoothly scroll the pages, hiding and showing scroll links appropriately jQuery('.ahead, .rewind, .firstlink').click(function() { // alert (this); }).smoothScroll({direction:'left', beforeScroll:function() { isScrolling = true; }, afterScroll:function() { linkVisibility(this); isScrolling = false; lastSmoothScroll = Date.now(); }}); function showNavForClosestAnchor () { var closest; if((jQuery(window).scrollLeft() + jQuery(window).width()) >= (jQuery(document).width()-20)) { // closest = jQuery('.chapternav-next'); closest = '#next-chapter'; } else { closest = '#'+findClosestAnchor(anchors).getAttribute('id'); } linkVisibility(closest); } var anchors = document.body.querySelectorAll('article a[id]'); jQuery('.ahead, .rewind').hide(); showNavForClosestAnchor(); // show appropriate links after manual scrolls too jQuery(window).scrollStopped(function() { if ( (false == isScrolling) && (Date.now() - lastSmoothScroll) > 5 ) { showNavForClosestAnchor(); } });