/*
                       _ _ _____                      _   _
                      | | |  __ \                    | | (_)
    ___  ___ _ __ ___ | | | |__) |_____   _____  __ _| |  _ ___
   / __|/ __| '__/ _ \| | |  _  // _ \ \ / / _ \/ _` | | | / __|
   \__ \ (__| | | (_) | | | | \ \  __/\ V /  __/ (_| | |_| \__ \
   |___/\___|_|  \___/|_|_|_|  \_\___| \_/ \___|\__,_|_(_) |___/ v.0.1.3
                                                        _/ |
                                                       |__/

    "Declarative on-scroll reveal animations."

/*=============================================================================

    scrollReveal.js was inspired by cbpScroller.js (c) 2014 Codrops.

    Licensed under the MIT license.
    http://www.opensource.org/licenses/mit-license.php

=============================================================================*/

/*! scrollReveal.js v0.1.3 (c) 2014 Julian Lloyd | MIT license */

/*===========================================================================*/


window.scrollReveal = (function (window) {

    'use strict';

    // generator (increments) for the next scroll-reveal-id
    var nextId = 1;

    /**
     * RequestAnimationFrame polyfill
     * @function
     * @private
     */
    var requestAnimFrame = (function () {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            };
    }());

    function scrollReveal(options) {

        this.options = this.extend(this.defaults, options);
        this.docElem = this.options.elem;
        this.styleBank = {};

        if (this.options.init == true) this.init();
    }

    scrollReveal.prototype = {

        defaults: {
            after:   '0s',
            enter:   'bottom',
            move:    '24px',
            over:    '0.66s',
            easing:  'ease-in-out',
            opacity: 0,
            complete: function() {},

            //  if 0, the element is considered in the viewport as soon as it enters
            //  if 1, the element is considered in the viewport when it's fully visible
            viewportFactor: 0.33,

            // if false, animations occur only once
            // if true, animations occur each time an element enters the viewport
            reset: false,

            // if true, scrollReveal.init() is automaticaly called upon instantiation
            init: true,
            elem: window.document.documentElement
        },

        /*=============================================================================*/

        init: function () {

            this.scrolled = false;

            var self = this;

            //  Check DOM for the data-scrollReveal attribute
            //  and initialize all found elements.
            this.elems = Array.prototype.slice.call(this.docElem.querySelectorAll('[data-scroll-reveal]'));
            this.elems.forEach(function (el, i) {

                //  Capture original style attribute
                var id = el.getAttribute("data-scroll-reveal-id");
                if (!id) {
                    id = nextId++;
                    el.setAttribute("data-scroll-reveal-id", id);
                }
                if (!self.styleBank[id]) {
                    self.styleBank[id] = el.getAttribute('style');
                }

                self.update(el);
            });

            var scrollHandler = function (e) {
                // No changing, exit
                if (!self.scrolled) {
                    self.scrolled = true;
                    requestAnimFrame(function () {
                        self._scrollPage();
                    });
                }
            };

            var resizeHandler = function () {

                //  If we鈥檙e still waiting for settimeout, reset the timer.
                if (self.resizeTimeout) {
                    clearTimeout(self.resizeTimeout);
                }
                function delayed() {
                    self._scrollPage();
                    self.resizeTimeout = null;
                }
                self.resizeTimeout = setTimeout(delayed, 200);
            };

            // captureScroll
            if (this.docElem == window.document.documentElement) {
                window.addEventListener('scroll', scrollHandler, false);
                window.addEventListener('resize', resizeHandler, false);
            }
            else {
                this.docElem.addEventListener('scroll', scrollHandler, false);
            }
        },

        /*=============================================================================*/

        _scrollPage: function () {
            var self = this;

            this.elems.forEach(function (el, i) {
                self.update(el);
            });
            this.scrolled = false;
        },

        /*=============================================================================*/

        parseLanguage: function (el) {

            //  Splits on a sequence of one or more commas or spaces.
            var words = el.getAttribute('data-scroll-reveal').split(/[, ]+/),
                parsed = {};

            function filter (words) {
                var ret = [],

                    blacklist = [
                        "from",
                        "the",
                        "and",
                        "then",
                        "but",
                        "with"
                    ];

                words.forEach(function (word, i) {
                    if (blacklist.indexOf(word) > -1) {
                        return;
                    }
                    ret.push(word);
                });

                return ret;
            }

            words = filter(words);

            words.forEach(function (word, i) {

                switch (word) {
                    case "enter":
                        parsed.enter = words[i + 1];
                        return;

                    case "after":
                        parsed.after = words[i + 1];
                        return;

                    case "wait":
                        parsed.after = words[i + 1];
                        return;

                    case "move":
                        parsed.move = words[i + 1];
                        return;

                    case "ease":
                        parsed.move = words[i + 1];
                        parsed.ease = "ease";
                        return;

                    case "ease-in":
                        parsed.move = words[i + 1];
                        parsed.easing = "ease-in";
                        return;

                    case "ease-in-out":
                        parsed.move = words[i + 1];
                        parsed.easing = "ease-in-out";
                        return;

                    case "ease-out":
                        parsed.move = words[i + 1];
                        parsed.easing = "ease-out";
                        return;

                    case "over":
                        parsed.over = words[i + 1];
                        return;

                    default:
                        return;
                }
            });

            return parsed;
        },


        /*=============================================================================*/

        update: function (el) {

            var that = this;
            var css   = this.genCSS(el);
            var style = this.styleBank[el.getAttribute("data-scroll-reveal-id")];

            if (style != null) style += ";"; else style = "";

            if (!el.getAttribute('data-scroll-reveal-initialized')) {
                el.setAttribute('style', style + css.initial);
                el.setAttribute('data-scroll-reveal-initialized', true);
            }

            if (!this.isElementInViewport(el, this.options.viewportFactor)) {
                if (this.options.reset) {
                    el.setAttribute('style', style + css.initial + css.reset);
                }
                return;
            }

            if (el.getAttribute('data-scroll-reveal-complete')) return;

            if (this.isElementInViewport(el, this.options.viewportFactor)) {
                el.setAttribute('style', style + css.target + css.transition);
                //  Without reset enabled, we can safely remove the style tag
                //  to prevent CSS specificy wars with authored CSS.
                if (!this.options.reset) {
                    setTimeout(function () {
                        if (style != "") {
                            el.setAttribute('style', style);
                        } else {
                            el.removeAttribute('style');
                        }
                        el.setAttribute('data-scroll-reveal-complete',true);
                        that.options.complete(el);
                    }, css.totalDuration);
                }
                return;
            }
        },

        /*=============================================================================*/

        genCSS: function (el) {
            var parsed = this.parseLanguage(el),
                enter,
                axis;

            if (parsed.enter) {

                if (parsed.enter == "top" || parsed.enter == "bottom") {
                    enter = parsed.enter;
                    axis = "y";
                }

                if (parsed.enter == "left" || parsed.enter == "right") {
                    enter = parsed.enter;
                    axis = "x";
                }

            } else {

                if (this.options.enter == "top" || this.options.enter == "bottom") {
                    enter = this.options.enter
                    axis = "y";
                }

                if (this.options.enter == "left" || this.options.enter == "right") {
                    enter = this.options.enter
                    axis = "x";
                }
            }

            //  After all values are parsed, let鈥檚 make sure our our
            //  pixel distance is negative for top and left entrances.
            //
            //  ie. "move 25px from top" starts at 'top: -25px' in CSS.

            if (enter == "top" || enter == "left") {
                if (parsed.move) {
                    parsed.move = "-" + parsed.move;
                }
                else {
                    parsed.move = "-" + this.options.move;
                }
            }

            var dist    = parsed.move    || this.options.move,
                dur     = parsed.over    || this.options.over,
                delay   = parsed.after   || this.options.after,
                easing  = parsed.easing  || this.options.easing,
                opacity = parsed.opacity || this.options.opacity;

            var transition = "-webkit-transition: -webkit-transform " + dur + " " + easing + " " + delay + ",  opacity " + dur + " " + easing + " " + delay + ";" +
                "transition: transform " + dur + " " + easing + " " + delay + ", opacity " + dur + " " + easing + " " + delay + ";" +
                "-webkit-perspective: 1000;" +
                "-webkit-backface-visibility: hidden;";

            //  The same as transition, but removing the delay for elements fading out.
            var reset = "-webkit-transition: -webkit-transform " + dur + " " + easing + " 0s,  opacity " + dur + " " + easing + " " + delay + ";" +
                "transition: transform " + dur + " " + easing + " 0s,  opacity " + dur + " " + easing + " " + delay + ";" +
                "-webkit-perspective: 1000;" +
                "-webkit-backface-visibility: hidden;";

            var initial = "-webkit-transform: translate" + axis + "(" + dist + ");" +
                "transform: translate" + axis + "(" + dist + ");" +
                "opacity: " + opacity + ";";

            var target = "-webkit-transform: translate" + axis + "(0);" +
                "transform: translate" + axis + "(0);" +
                "opacity: 1;";
            return {
                transition: transition,
                initial: initial,
                target: target,
                reset: reset,
                totalDuration: ((parseFloat(dur) + parseFloat(delay)) * 1000)
            };
        },

        getViewportH : function () {
            var client = this.docElem['clientHeight'],
                inner = window['innerHeight'];

            if (this.docElem == window.document.documentElement)
                return (client < inner) ? inner : client;
            else
                return client;
        },

        getOffset : function(el) {
            var offsetTop = 0,
                offsetLeft = 0;

            do {
                if (!isNaN(el.offsetTop)) {
                    offsetTop += el.offsetTop;
                }
                if (!isNaN(el.offsetLeft)) {
                    offsetLeft += el.offsetLeft;
                }
            } while (el = el.offsetParent)

            return {
                top: offsetTop,
                left: offsetLeft
            }
        },

        isElementInViewport : function(el, h) {
            var scrolled = this.docElem.scrollTop + this.docElem.offsetTop;
            if (this.docElem == window.document.documentElement)scrolled = window.pageYOffset;
            var
                viewed = scrolled + this.getViewportH(),
                elH = el.offsetHeight,
                elTop = this.getOffset(el).top,
                elBottom = elTop + elH,
                h = h || 0;

            return (elTop + elH * h) <= viewed
                && (elBottom) >= scrolled
                || (el.currentStyle? el.currentStyle : window.getComputedStyle(el, null)).position == 'fixed';
        },

        extend: function (a, b){
            for (var key in b) {
                if (b.hasOwnProperty(key)) {

                    a[key] = b[key];
                }
            }
            return a;
        }
    }; // end scrollReveal.prototype

    return scrollReveal;
})(window);