//// SmoothScroll for websites v1.4.6 (Balazs Galambosi)// http://www.smoothscroll.net///// Licensed under the terms of the MIT license.//// You may use it in your theme if you credit me. // It is also free to use on any individual website.//// Exception:// The only restriction is to not publish any // extension for browsers or native application// without getting a written permission first.//(function(){// Scroll Variables (tweakable)vardefaultOptions={// Scrolling CoreframeRate:150,// [Hz]animationTime:400,// [ms]stepSize:100,// [px]// Pulse (less tweakable)// ratio of "tail" to "acceleration"pulseAlgorithm:true,pulseScale:4,pulseNormalize:1,// AccelerationaccelerationDelta:50,// 50accelerationMax:3,// 3// Keyboard SettingskeyboardSupport:true,// optionarrowScroll:50,// [px]// OtherfixedBackground:true,excluded:''};varoptions=defaultOptions;// Other VariablesvarisExcluded=false;varisFrame=false;vardirection={x:0,y:0};varinitDone=false;varroot=document.documentElement;varactiveElement;varobserver;varrefreshSize;vardeltaBuffer=[];varisMac=/^Mac/.test(navigator.platform);varkey={left:37,up:38,right:39,down:40,spacebar:32,pageup:33,pagedown:34,end:35,home:36};vararrowKeys={37:1,38:1,39:1,40:1};/***********************************************
* INITIALIZE
***********************************************//**
* Tests if smooth scrolling is allowed. Shuts down everything if not.
*/functioninitTest(){if(options.keyboardSupport){addEvent('keydown',keydown);}}/**
* Sets up scrolls array, determines if frames are involved.
*/functioninit(){if(initDone||!document.body)return;initDone=true;varbody=document.body;varhtml=document.documentElement;varwindowHeight=window.innerHeight;varscrollHeight=body.scrollHeight;// check compat mode for root elementroot=(document.compatMode.indexOf('CSS')>=0)?html:body;activeElement=body;initTest();// Checks if this script is running in a frameif(top!=self){isFrame=true;}/**
* Safari 10 fixed it, Chrome fixed it in v45:
* This fixes a bug where the areas left and right to
* the content does not trigger the onmousewheel event
* on some pages. e.g.: html, body { height: 100% }
*/elseif(isOldSafari&&scrollHeight>windowHeight&&(body.offsetHeight<=windowHeight||html.offsetHeight<=windowHeight)){varfullPageElem=document.createElement('div');fullPageElem.style.cssText='position:absolute; z-index:-10000; '+'top:0; left:0; right:0; height:'+root.scrollHeight+'px';document.body.appendChild(fullPageElem);// DOM changed (throttled) to fix heightvarpendingRefresh;refreshSize=function(){if(pendingRefresh)return;// could also be: clearTimeout(pendingRefresh);pendingRefresh=setTimeout(function(){if(isExcluded)return;// could be running after cleanupfullPageElem.style.height='0';fullPageElem.style.height=root.scrollHeight+'px';pendingRefresh=null;},500);// act rarely to stay fast};setTimeout(refreshSize,10);addEvent('resize',refreshSize);// TODO: attributeFilter?varconfig={attributes:true,childList:true,characterData:false// subtree: true};observer=newMutationObserver(refreshSize);observer.observe(body,config);if(root.offsetHeight<=windowHeight){varclearfix=document.createElement('div');clearfix.style.clear='both';body.appendChild(clearfix);}}// disable fixed backgroundif(!options.fixedBackground&&!isExcluded){body.style.backgroundAttachment='scroll';html.style.backgroundAttachment='scroll';}}/**
* Removes event listeners and other traces left on the page.
*/functioncleanup(){observer&&observer.disconnect();removeEvent(wheelEvent,wheel);removeEvent('mousedown',mousedown);removeEvent('keydown',keydown);removeEvent('resize',refreshSize);removeEvent('load',init);}/************************************************
* SCROLLING
************************************************/varque=[];varpending=false;varlastScroll=Date.now();/**
* Pushes scroll actions to the scrolling queue.
*/functionscrollArray(elem,left,top){directionCheck(left,top);if(options.accelerationMax!=1){varnow=Date.now();varelapsed=now-lastScroll;if(elapsed<options.accelerationDelta){varfactor=(1+(50/elapsed))/2;if(factor>1){factor=Math.min(factor,options.accelerationMax);left*=factor;top*=factor;}}lastScroll=Date.now();}// push a scroll commandque.push({x:left,y:top,lastX:(left<0)?0.99:-0.99,lastY:(top<0)?0.99:-0.99,start:Date.now()});// don't act if there's a pending queueif(pending){return;}varscrollWindow=(elem===document.body);varstep=function(time){varnow=Date.now();varscrollX=0;varscrollY=0;for(vari=0;i<que.length;i++){varitem=que[i];varelapsed=now-item.start;varfinished=(elapsed>=options.animationTime);// scroll position: [0, 1]varposition=(finished)?1:elapsed/options.animationTime;// easing [optional]if(options.pulseAlgorithm){position=pulse(position);}// only need the differencevarx=(item.x*position-item.lastX)>>0;vary=(item.y*position-item.lastY)>>0;// add this to the total scrollingscrollX+=x;scrollY+=y;// update last valuesitem.lastX+=x;item.lastY+=y;// delete and step back if it's overif(finished){que.splice(i,1);i--;}}// scroll left and topif(scrollWindow){window.scrollBy(scrollX,scrollY);}else{if(scrollX)elem.scrollLeft+=scrollX;if(scrollY)elem.scrollTop+=scrollY;}// clean up if there's nothing left to doif(!left&&!top){que=[];}if(que.length){requestFrame(step,elem,(1000/options.frameRate+1));}else{pending=false;}};// start a new queue of actionsrequestFrame(step,elem,0);pending=true;}/***********************************************
* EVENTS
***********************************************//**
* Mouse wheel handler.
* @param {Object} event
*/functionwheel(event){if(!initDone){init();}vartarget=event.target;// leave early if default action is prevented // or it's a zooming event with CTRL if(event.defaultPrevented||event.ctrlKey){returntrue;}// leave embedded content alone (flash & pdf)if(isNodeName(activeElement,'embed')||(isNodeName(target,'embed')&&/\.pdf/i.test(target.src))||isNodeName(activeElement,'object')||target.shadowRoot){returntrue;}vardeltaX=-event.wheelDeltaX||event.deltaX||0;vardeltaY=-event.wheelDeltaY||event.deltaY||0;if(isMac){if(event.wheelDeltaX&&isDivisible(event.wheelDeltaX,120)){deltaX=-120*(event.wheelDeltaX/Math.abs(event.wheelDeltaX));}if(event.wheelDeltaY&&isDivisible(event.wheelDeltaY,120)){deltaY=-120*(event.wheelDeltaY/Math.abs(event.wheelDeltaY));}}// use wheelDelta if deltaX/Y is not availableif(!deltaX&&!deltaY){deltaY=-event.wheelDelta||0;}// line based scrolling (Firefox mostly)if(event.deltaMode===1){deltaX*=40;deltaY*=40;}varoverflowing=overflowingAncestor(target);// nothing to do if there's no element that's scrollableif(!overflowing){// except Chrome iframes seem to eat wheel events, which we need to // propagate up, if the iframe has nothing overflowing to scrollif(isFrame&&isChrome){// change target to iframe element itself for the parent frameObject.defineProperty(event,"target",{value:window.frameElement});returnparent.wheel(event);}returntrue;}// check if it's a touchpad scroll that should be ignoredif(isTouchpad(deltaY)){returntrue;}// scale by step size// delta is 120 most of the time// synaptics seems to send 1 sometimesif(Math.abs(deltaX)>1.2){deltaX*=options.stepSize/120;}if(Math.abs(deltaY)>1.2){deltaY*=options.stepSize/120;}scrollArray(overflowing,deltaX,deltaY);event.preventDefault();scheduleClearCache();}/**
* Keydown event handler.
* @param {Object} event
*/functionkeydown(event){vartarget=event.target;varmodifier=event.ctrlKey||event.altKey||event.metaKey||(event.shiftKey&&event.keyCode!==key.spacebar);// our own tracked active element could've been removed from the DOMif(!document.body.contains(activeElement)){activeElement=document.activeElement;}// do nothing if user is editing text// or using a modifier key (except shift)// or in a dropdown// or inside interactive elementsvarinputNodeNames=/^(textarea|select|embed|object)$/i;varbuttonTypes=/^(button|submit|radio|checkbox|file|color|image)$/i;if(event.defaultPrevented||inputNodeNames.test(target.nodeName)||isNodeName(target,'input')&&!buttonTypes.test(target.type)||isNodeName(activeElement,'video')||isInsideYoutubeVideo(event)||target.isContentEditable||modifier){returntrue;}// [spacebar] should trigger button press, leave it aloneif((isNodeName(target,'button')||isNodeName(target,'input')&&buttonTypes.test(target.type))&&event.keyCode===key.spacebar){returntrue;}// [arrwow keys] on radio buttons should be left aloneif(isNodeName(target,'input')&&target.type=='radio'&&arrowKeys[event.keyCode]){returntrue;}varshift,x=0,y=0;varoverflowing=overflowingAncestor(activeElement);if(!overflowing){// Chrome iframes seem to eat key events, which we need to // propagate up, if the iframe has nothing overflowing to scrollreturn(isFrame&&isChrome)?parent.keydown(event):true;}varclientHeight=overflowing.clientHeight;if(overflowing==document.body){clientHeight=window.innerHeight;}switch(event.keyCode){casekey.up:y=-options.arrowScroll;break;casekey.down:y=options.arrowScroll;break;casekey.spacebar:// (+ shift)shift=event.shiftKey?1:-1;y=-shift*clientHeight*0.9;break;casekey.pageup:y=-clientHeight*0.9;break;casekey.pagedown:y=clientHeight*0.9;break;casekey.home:y=-overflowing.scrollTop;break;casekey.end:varscroll=overflowing.scrollHeight-overflowing.scrollTop;varscrollRemaining=scroll-clientHeight;y=(scrollRemaining>0)?scrollRemaining+10:0;break;casekey.left:x=-options.arrowScroll;break;casekey.right:x=options.arrowScroll;break;default:returntrue;// a key we don't care about}scrollArray(overflowing,x,y);event.preventDefault();scheduleClearCache();}/**
* Mousedown event only for updating activeElement
*/functionmousedown(event){activeElement=event.target;}/***********************************************
* OVERFLOW
***********************************************/varuniqueID=(function(){vari=0;returnfunction(el){returnel.uniqueID||(el.uniqueID=i++);};})();varcache={};// cleared out after a scrolling sessionvarclearCacheTimer;//setInterval(function () { cache = {}; }, 10 * 1000);functionscheduleClearCache(){clearTimeout(clearCacheTimer);clearCacheTimer=setInterval(function(){cache={};},1*1000);}functionsetCache(elems,overflowing){for(vari=elems.length;i--;)cache[uniqueID(elems[i])]=overflowing;returnoverflowing;}// (body) (root)// | hidden | visible | scroll | auto |// hidden | no | no | YES | YES |// visible | no | YES | YES | YES |// scroll | no | YES | YES | YES |// auto | no | YES | YES | YES |functionoverflowingAncestor(el){varelems=[];varbody=document.body;varrootScrollHeight=root.scrollHeight;do{varcached=cache[uniqueID(el)];if(cached){returnsetCache(elems,cached);}elems.push(el);if(rootScrollHeight===el.scrollHeight){vartopOverflowsNotHidden=overflowNotHidden(root)&&overflowNotHidden(body);varisOverflowCSS=topOverflowsNotHidden||overflowAutoOrScroll(root);if(isFrame&&isContentOverflowing(root)||!isFrame&&isOverflowCSS){returnsetCache(elems,getScrollRoot());}}elseif(isContentOverflowing(el)&&overflowAutoOrScroll(el)){returnsetCache(elems,el);}}while(el=el.parentElement);}functionisContentOverflowing(el){return(el.clientHeight+10<el.scrollHeight);}// typically for <body> and <html>functionoverflowNotHidden(el){varoverflow=getComputedStyle(el,'').getPropertyValue('overflow-y');return(overflow!=='hidden');}// for all other elementsfunctionoverflowAutoOrScroll(el){varoverflow=getComputedStyle(el,'').getPropertyValue('overflow-y');return(overflow==='scroll'||overflow==='auto');}/***********************************************
* HELPERS
***********************************************/functionaddEvent(type,fn){window.addEventListener(type,fn,false);}functionremoveEvent(type,fn){window.removeEventListener(type,fn,false);}functionisNodeName(el,tag){return(el.nodeName||'').toLowerCase()===tag.toLowerCase();}functiondirectionCheck(x,y){x=(x>0)?1:-1;y=(y>0)?1:-1;if(direction.x!==x||direction.y!==y){direction.x=x;direction.y=y;que=[];lastScroll=0;}}vardeltaBufferTimer;if(window.localStorage&&localStorage.SS_deltaBuffer){try{// #46 Safari throws in private browsing for localStorage deltaBuffer=localStorage.SS_deltaBuffer.split(',');}catch(e){}}functionisTouchpad(deltaY){if(!deltaY)return;if(!deltaBuffer.length){deltaBuffer=[deltaY,deltaY,deltaY];}deltaY=Math.abs(deltaY);deltaBuffer.push(deltaY);deltaBuffer.shift();clearTimeout(deltaBufferTimer);deltaBufferTimer=setTimeout(function(){try{// #46 Safari throws in private browsing for localStoragelocalStorage.SS_deltaBuffer=deltaBuffer.join(',');}catch(e){}},1000);return!allDeltasDivisableBy(120)&&!allDeltasDivisableBy(100);}functionisDivisible(n,divisor){return(Math.floor(n/divisor)==n/divisor);}functionallDeltasDivisableBy(divisor){return(isDivisible(deltaBuffer[0],divisor)&&isDivisible(deltaBuffer[1],divisor)&&isDivisible(deltaBuffer[2],divisor));}functionisInsideYoutubeVideo(event){varelem=event.target;varisControl=false;if(document.URL.indexOf('www.youtube.com/watch')!=-1){do{isControl=(elem.classList&&elem.classList.contains('html5-video-controls'));if(isControl)break;}while(elem=elem.parentNode);}returnisControl;}varrequestFrame=(function(){return(window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback,element,delay){window.setTimeout(callback,delay||(1000/60));});})();varMutationObserver=(window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver);vargetScrollRoot=(function(){varSCROLL_ROOT;returnfunction(){if(!SCROLL_ROOT){vardummy=document.createElement('div');dummy.style.cssText='height:10000px;width:1px;';document.body.appendChild(dummy);varbodyScrollTop=document.body.scrollTop;vardocElScrollTop=document.documentElement.scrollTop;window.scrollBy(0,3);if(document.body.scrollTop!=bodyScrollTop)(SCROLL_ROOT=document.body);else(SCROLL_ROOT=document.documentElement);window.scrollBy(0,-3);document.body.removeChild(dummy);}returnSCROLL_ROOT;};})();/***********************************************
* PULSE (by Michael Herf)
***********************************************//**
* Viscous fluid with a pulse for part and decay for the rest.
* - Applies a fixed force over an interval (a damped acceleration), and
* - Lets the exponential bleed away the velocity over a longer interval
* - Michael Herf, http://stereopsis.com/stopping/
*/functionpulse_(x){varval,start,expx;// testx=x*options.pulseScale;if(x<1){// acceleartionval=x-(1-Math.exp(-x));}else{// tail// the previous animation ended here:start=Math.exp(-1);// simple viscous dragx-=1;expx=1-Math.exp(-x);val=start+(expx*(1-start));}returnval*options.pulseNormalize;}functionpulse(x){if(x>=1)return1;if(x<=0)return0;if(options.pulseNormalize==1){options.pulseNormalize/=pulse_(1);}returnpulse_(x);}/***********************************************
* FIRST RUN
***********************************************/varuserAgent=window.navigator.userAgent;varisEdge=/Edge/.test(userAgent);// thank you MSvarisChrome=/chrome/i.test(userAgent)&&!isEdge;varisSafari=/safari/i.test(userAgent)&&!isEdge;varisMobile=/mobile/i.test(userAgent);varisIEWin7=/Windows NT 6.1/i.test(userAgent)&&/rv:11/i.test(userAgent);varisOldSafari=isSafari&&(/Version\/8/i.test(userAgent)||/Version\/9/i.test(userAgent));varisEnabledForBrowser=(isChrome||isSafari||isIEWin7)&&!isMobile;varwheelEvent;if('onwheel'indocument.createElement('div'))wheelEvent='wheel';elseif('onmousewheel'indocument.createElement('div'))wheelEvent='mousewheel';if(wheelEvent&&isEnabledForBrowser){addEvent(wheelEvent,wheel);addEvent('mousedown',mousedown);addEvent('load',init);}/***********************************************
* PUBLIC INTERFACE
***********************************************/functionSmoothScroll(optionsToSet){for(varkeyinoptionsToSet)if(defaultOptions.hasOwnProperty(key))options[key]=optionsToSet[key];}SmoothScroll.destroy=cleanup;if(window.SmoothScrollOptions)// async APISmoothScroll(window.SmoothScrollOptions);if(typeofdefine==='function'&&define.amd)define(function(){returnSmoothScroll;});elseif('object'==typeofexports)module.exports=SmoothScroll;elsewindow.SmoothScroll=SmoothScroll;})();