Template:BIT-China/js-jquery.smooth-scroll

/*!

* 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 <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);