/cody/learning-zone/website-templates/johndoe-portfolio-resume-bootstrap-template/js/smoothscroll.js
// SmoothScroll for websites v1.2.1// Licensed under the terms of the MIT license.
// People involved//  - Balazs Galambosi (maintainer)  //  - Michael Herf     (Pulse Algorithm)
(function(){  // Scroll Variables (tweakable)var defaultOptions = {
    // Scrolling Core    frameRate        : 150, // [Hz]    animationTime    : 400, // [px]    stepSize         : 120, // [px]
    // Pulse (less tweakable)    // ratio of "tail" to "acceleration"    pulseAlgorithm   : true,    pulseScale       : 8,    pulseNormalize   : 1,
    // Acceleration    accelerationDelta : 20,  // 20    accelerationMax   : 1,   // 1
    // Keyboard Settings    keyboardSupport   : true,  // option    arrowScroll       : 50,     // [px]
    // Other    touchpadSupport   : true,    fixedBackground   : true,     excluded          : ""    };
var options = defaultOptions;

// Other Variablesvar isExcluded = false;var isFrame = false;var direction = { x: 0, y: 0 };var initDone  = false;var root = document.documentElement;var activeElement;var observer;var deltaBuffer = [ 120, 120, 120 ];
var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,             pageup: 33, pagedown: 34, end: 35, home: 36 };

/*********************************************** * SETTINGS ***********************************************/
var options = defaultOptions;

/*********************************************** * INITIALIZE ***********************************************/
/** * Tests if smooth scrolling is allowed. Shuts down everything if not. */function initTest() {
    var disableKeyboard = false;         // disable keyboard support if anything above requested it    if (disableKeyboard) {        removeEvent("keydown", keydown);    }
    if (options.keyboardSupport && !disableKeyboard) {        addEvent("keydown", keydown);    }}
/** * Sets up scrolls array, determines if frames are involved. */function init() {      if (!document.body) return;
    var body = document.body;    var html = document.documentElement;    var windowHeight = window.innerHeight;     var scrollHeight = body.scrollHeight;        // check compat mode for root element    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;    activeElement = body;        initTest();    initDone = true;
    // Checks if this script is running in a frame    if (top != self) {        isFrame = true;    }
    /**     * 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% }     */    else if (scrollHeight > windowHeight &&            (body.offsetHeight <= windowHeight ||              html.offsetHeight <= windowHeight)) {
        html.style.height = 'auto';        setTimeout(refresh, 10);
        // clearfix        if (root.offsetHeight <= windowHeight) {            var underlay = document.createElement("div"); 	            underlay.style.clear = "both";            body.appendChild(underlay);        }    }
    // disable fixed background    if (!options.fixedBackground && !isExcluded) {        body.style.backgroundAttachment = "scroll";        html.style.backgroundAttachment = "scroll";    }}

/************************************************ * SCROLLING  ************************************************/ var que = [];var pending = false;var lastScroll = +new Date;
/** * Pushes scroll actions to the scrolling queue. */function scrollArray(elem, left, top, delay) {        delay || (delay = 1000);    directionCheck(left, top);
    if (options.accelerationMax != 1) {        var now = +new Date;        var elapsed = now - lastScroll;        if (elapsed < options.accelerationDelta) {            var factor = (1 + (30 / elapsed)) / 2;            if (factor > 1) {                factor = Math.min(factor, options.accelerationMax);                left *= factor;                top  *= factor;            }        }        lastScroll = +new Date;    }                  // push a scroll command    que.push({        x: left,         y: top,         lastX: (left < 0) ? 0.99 : -0.99,        lastY: (top  < 0) ? 0.99 : -0.99,         start: +new Date    });            // don't act if there's a pending queue    if (pending) {        return;    }  
    var scrollWindow = (elem === document.body);        var step = function (time) {                var now = +new Date;        var scrollX = 0;        var scrollY = 0;             for (var i = 0; i < que.length; i++) {                        var item = que[i];            var elapsed  = now - item.start;            var finished = (elapsed >= options.animationTime);                        // scroll position: [0, 1]            var position = (finished) ? 1 : elapsed / options.animationTime;                        // easing [optional]            if (options.pulseAlgorithm) {                position = pulse(position);            }                        // only need the difference            var x = (item.x * position - item.lastX) >> 0;            var y = (item.y * position - item.lastY) >> 0;                        // add this to the total scrolling            scrollX += x;            scrollY += y;                                    // update last values            item.lastX += x;            item.lastY += y;                    // delete and step back if it's over            if (finished) {                que.splice(i, 1); i--;            }                   }
        // scroll left and top        if (scrollWindow) {            window.scrollBy(scrollX, scrollY);        }         else {            if (scrollX) elem.scrollLeft += scrollX;            if (scrollY) elem.scrollTop  += scrollY;                            }                // clean up if there's nothing left to do        if (!left && !top) {            que = [];        }                if (que.length) {             requestFrame(step, elem, (delay / options.frameRate + 1));         } else {             pending = false;        }    };        // start a new queue of actions    requestFrame(step, elem, 0);    pending = true;}

/*********************************************** * EVENTS ***********************************************/
/** * Mouse wheel handler. * @param {Object} event */function wheel(event) {
    if (!initDone) {        init();    }        var target = event.target;    var overflowing = overflowingAncestor(target);        // use default if there's no overflowing    // element or default action is prevented        if (!overflowing || event.defaultPrevented ||        isNodeName(activeElement, "embed") ||       (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {        return true;    }
    var deltaX = event.wheelDeltaX || 0;    var deltaY = event.wheelDeltaY || 0;        // use wheelDelta if deltaX/Y is not available    if (!deltaX && !deltaY) {        deltaY = event.wheelDelta || 0;    }
    // check if it's a touchpad scroll that should be ignored    if (!options.touchpadSupport && isTouchpad(deltaY)) {        return true;    }
    // scale by step size    // delta is 120 most of the time    // synaptics seems to send 1 sometimes    if (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();}
/** * Keydown event handler. * @param {Object} event */function keydown(event) {
    var target   = event.target;    var modifier = event.ctrlKey || event.altKey || event.metaKey ||                   (event.shiftKey && event.keyCode !== key.spacebar);        // do nothing if user is editing text    // or using a modifier key (except shift)    // or in a dropdown    if ( /input|textarea|select|embed/i.test(target.nodeName) ||         target.isContentEditable ||          event.defaultPrevented   ||         modifier ) {      return true;    }    // spacebar should trigger button press    if (isNodeName(target, "button") &&        event.keyCode === key.spacebar) {      return true;    }        var shift, x = 0, y = 0;    var elem = overflowingAncestor(activeElement);    var clientHeight = elem.clientHeight;
    if (elem == document.body) {        clientHeight = window.innerHeight;    }
    switch (event.keyCode) {        case key.up:            y = -options.arrowScroll;            break;        case key.down:            y = options.arrowScroll;            break;                 case key.spacebar: // (+ shift)            shift = event.shiftKey ? 1 : -1;            y = -shift * clientHeight * 0.9;            break;        case key.pageup:            y = -clientHeight * 0.9;            break;        case key.pagedown:            y = clientHeight * 0.9;            break;        case key.home:            y = -elem.scrollTop;            break;        case key.end:            var damt = elem.scrollHeight - elem.scrollTop - clientHeight;            y = (damt > 0) ? damt+10 : 0;            break;        case key.left:            x = -options.arrowScroll;            break;        case key.right:            x = options.arrowScroll;            break;                    default:            return true; // a key we don't care about    }
    scrollArray(elem, x, y);    event.preventDefault();}
/** * Mousedown event only for updating activeElement */function mousedown(event) {    activeElement = event.target;}

/*********************************************** * OVERFLOW ***********************************************/ var cache = {}; // cleared out every once in whilesetInterval(function () { cache = {}; }, 10 * 1000);
var uniqueID = (function () {    var i = 0;    return function (el) {        return el.uniqueID || (el.uniqueID = i++);    };})();
function setCache(elems, overflowing) {    for (var i = elems.length; i--;)        cache[uniqueID(elems[i])] = overflowing;    return overflowing;}
function overflowingAncestor(el) {    var elems = [];    var rootScrollHeight = root.scrollHeight;    do {        var cached = cache[uniqueID(el)];        if (cached) {            return setCache(elems, cached);        }        elems.push(el);        if (rootScrollHeight === el.scrollHeight) {            if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {                return setCache(elems, document.body); // scrolling root in WebKit            }        } else if (el.clientHeight + 10 < el.scrollHeight) {            overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");            if (overflow === "scroll" || overflow === "auto") {                return setCache(elems, el);            }        }    } while (el = el.parentNode);}

/*********************************************** * HELPERS ***********************************************/
function addEvent(type, fn, bubble) {    window.addEventListener(type, fn, (bubble||false));}
function removeEvent(type, fn, bubble) {    window.removeEventListener(type, fn, (bubble||false));  }
function isNodeName(el, tag) {    return (el.nodeName||"").toLowerCase() === tag.toLowerCase();}
function directionCheck(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;    }}
var deltaBufferTimer;
function isTouchpad(deltaY) {    if (!deltaY) return;    deltaY = Math.abs(deltaY)    deltaBuffer.push(deltaY);    deltaBuffer.shift();    clearTimeout(deltaBufferTimer);
    var allEquals    = (deltaBuffer[0] == deltaBuffer[1] &&                         deltaBuffer[1] == deltaBuffer[2]);    var allDivisable = (isDivisible(deltaBuffer[0], 120) &&                        isDivisible(deltaBuffer[1], 120) &&                        isDivisible(deltaBuffer[2], 120));    return !(allEquals || allDivisable);} 
function isDivisible(n, divisor) {    return (Math.floor(n / divisor) == n / divisor);}
var requestFrame = (function () {      return  window.requestAnimationFrame       ||               window.webkitRequestAnimationFrame ||               function (callback, element, delay) {                  window.setTimeout(callback, delay || (1000/60));              };})();

/*********************************************** * PULSE ***********************************************/ /** * 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/ */function pulse_(x) {    var val, start, expx;    // test    x = x * options.pulseScale;    if (x < 1) { // acceleartion        val = x - (1 - Math.exp(-x));    } else {     // tail        // the previous animation ended here:        start = Math.exp(-1);        // simple viscous drag        x -= 1;        expx = 1 - Math.exp(-x);        val = start + (expx * (1 - start));    }    return val * options.pulseNormalize;}
function pulse(x) {    if (x >= 1) return 1;    if (x <= 0) return 0;
    if (options.pulseNormalize == 1) {        options.pulseNormalize /= pulse_(1);    }    return pulse_(x);}
var isChrome = /chrome/i.test(window.navigator.userAgent);var isMouseWheelSupported = 'onmousewheel' in document; 
if (isMouseWheelSupported && isChrome) {	addEvent("mousedown", mousedown);	addEvent("mousewheel", wheel);	addEvent("load", init);};
})();