// OPP
window.OPP = window.OPP || {};

// Modernizr Tests
(function (w, d) {
    if (!w.Modernizr) { return; }

    // Detect support for media queries and layouts for use by js and css
    Modernizr.addTest('mediaquery', Modernizr.mq( OPP.modernMQ ));

    // Detect stickyhover using a crude regex to detect known ios devices
    Modernizr.addTest('stickyhover', Modernizr.touch && !!navigator.userAgent.match(/\biP(ad|hone|od)\b/i));

    // Detect flexbox layout bug in Safari (See #520)
    Modernizr.testStyles('#modernizr{display:flex;flex-wrap:wrap;width:2px;}#modernizr1,#modernizr2{flex:none;min-width:2px;width:1px;height:1px}', function (elem) {
        var fixed = elem.firstChild.offsetTop !== elem.lastChild.offsetTop;
        Modernizr.addTest('webkit-bug-136041', !fixed);
    }, 2);

})(window, window.document);

// OPP.ajax (also used in contact.js)
(function (w) {
    var OPP = w.OPP;

    function noop() {}

    function xhrSettings(options) {
        // default settings
        var settings = {
            async: true,
            data: null, // no data
            error: noop,
            method: 'GET',
            timeout: 0, // no timeout
            success: noop
        };

        // shallow copy
        for (prop in options) {
            if( options.hasOwnProperty(prop) ) {
                settings[prop] = options[prop];
            }
        }

        // normalize method name
        settings.method = settings.method.toUpperCase();

        return settings;
    }

    OPP.ajax = function (options) {
        var xhr = new XMLHttpRequest();
        var settings = xhrSettings(options);
        var context = settings.context || settings;
        xhr.onreadystatechange = function (e) {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    settings.success.call(context, e);
                }
                else {
                    settings.error.call(context, e);
                }
            }
        }
        xhr.open(settings.method, settings.uri, settings.async);
        xhr.timeout = settings.timeout;
        xhr.send(settings.data);
    };
}(window));

// OPP.{has,add,remove,toggle}Class
(function (w) {
    var OPP = w.OPP;

    // copied from lazySizes v1.1.3, thanks :)
    var hasClass = OPP.hasClass = function(ele, cls) {
        var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
        return ele.className.match(reg) && reg;
    };

    // copied from lazySizes v1.1.3, thanks :)
    OPP.addClass = function(ele, cls) {
        if (!hasClass(ele, cls)){
            ele.className += ' '+cls;
        }
    };

    // copied from lazySizes v1.1.3, thanks :)
    OPP.removeClass = function(ele, cls) {
        var reg;
        if ((reg = hasClass(ele,cls))) {
            ele.className = ele.className.replace(reg, ' ');
        }
    };

    OPP.toggleClass = function(ele, cls) {
        var reg;
        if ((reg = hasClass(ele,cls))) {
            ele.className = ele.className.replace(reg, ' ');
        } else {
            ele.className += ' '+cls;
        }
    };
})(window);

// OPP Modal
(function (w, d) {
    var OPP = w.OPP;

    var hasClass = OPP.hasClass;
    var addClass = OPP.addClass;
    var removeClass = OPP.removeClass;
    if (!hasClass || !addClass || !removeClass) { return; }
    if (!'getElementsByClassName' in d) { return; }
    (function () {
        var Modal = OPP.Modal = OPP.Modal || {};

        var Class = {
            BODY: 'modal-open',
            MODAL: 'modal',
            SHOW: 'show',
        };

        function isOpen() {
            return hasClass(d.body, Class.BODY);
        }

        Modal.elems = d.getElementsByClassName(Class.MODAL);

        function hideAll() {
            var i = 0, elems = Modal.elems, len = elems.length;
            for (; i < len; i++) { removeClass(elems[i], Class.SHOW); }
        }

        function show(elem) {
            if ( !elem || !hasClass(elem, Class.MODAL) ) { return; }
            hideAll();
            addClass(elem, Class.SHOW);
            addClass(d.body, Class.BODY);
        };

        function hide() {
            removeClass(d.body, Class.BODY);
            hideAll();
        };

        OPP.isModalOpen = Modal.isOpen = isOpen;
        OPP.showModal = Modal.show = show;
        OPP.hideModal = Modal.hide = hide;

        addClass(d.documentElement, 'can-use-modal');
    })();
})(window, document);

// NOTE: This is intended to run on every page load for large AND small
// layouts. Do not be confused by the name of the file. It is loaded regardles
// of the device classification.

// handle redirect cookie used to trigger welcome page
(function (w, d) {
    var OPP = w.OPP;

    function hasCookie(name) {
        return d.cookie.match(new RegExp('\\b'+name+'='));
    }

    function killCookie(name) {
	    d.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/"
    }

    var addClass = OPP.addClass;

    if (!addClass) { return; }

    (function () {
        var rootRedirectCookie = '_opp-root-redirect';

        if (w.location.pathname == "/home.html" && hasCookie(rootRedirectCookie)) {
            killCookie(rootRedirectCookie);
            addClass(d.documentElement, 'is-root-redirect');
        }
    }());

}(window, window.document));

// NOTE: This is intended to run on every page load for large AND small
// layouts. Do not be confused by the name of the file. It is loaded regardles
// of the device classification.

// check .down and .grace_period
(function (w, d) {
    var OPP = w.OPP;

    var ajax = OPP.ajax;
    var addClass = OPP.addClass;
    var downURI = OPP.downURI;
    var gracePeriodURI = OPP.gracePeriodURI;

    if (!ajax || !addClass || !downURI || !gracePeriodURI) { return; }

    (function () {
        function takeDown() {
            window.scrollTo(0, 0); // required by offline css.
            addClass(d.documentElement, 'is-down');
        }

        function checkGracePeriod() {
            ajax({
                uri: gracePeriodURI,
                timeout: 1000,
                error: takeDown
            });
        }

        function checkDown() {
            ajax({
                uri: downURI,
                timeout: 1000,
                success: checkGracePeriod
            });
        }

        // autorun
        checkDown();
    }());
}(window, window.document));

// OPP.{on,}refreshLayout
(function (w) {
    var OPP = w.OPP;

    var queue = [];
    var length = queue.length;

    OPP.onRefreshLayout = function (f) {
        length = queue.push(f);
    };

    OPP.refreshLayout = function () {
        for(var i = 0; i < length; i++) {
            queue[i]();
        }
    };
})(window);

// OPP.masonryInit
// TODO: split this module for clarity
(function (w, d) {
    var OPP = w.OPP;
    var docElem = d.documentElement;
    var transEndEventNames = {
        'WebkitTransition' : 'webkitTransitionEnd',// Saf 6, Android Browser
        'MozTransition'    : 'transitionend',      // only for FF < 15
        'transition'       : 'transitionend'       // IE10, Opera, Chrome, FF 15+, Saf 7+
    };
    var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];

    var hasClass = OPP.hasClass;
    var addClass = OPP.addClass;
    var removeClass = OPP.removeClass;
    var refreshLayout = OPP.refreshLayout;
    var onRefreshLayout = OPP.onRefreshLayout;

    // do nothing if Masonry or required DOM features are missing
    if (!w.Masonry || !w.addEventListener || !w.getComputedStyle || !d.querySelector || !d.getElementsByTagName) {
        return;
    }

    function debounce(f, delay) {
        var timer, run = function (context, args) {
            f.apply(context, args);
        };

        return function () {
            w.clearTimeout(timer);
            timer = w.setTimeout(run, delay, this, arguments);
        };
    }

    function masonryAddZIndex(items) {
        var colXs = {}, i = this.cols - 1;
        while (i >= 0) {
            colXs[Math.floor(i * this.columnWidth)] = i;
            i--;
        }

        // add z-index to each item to prevent staggered overlap.
        items.forEach(function(item) {
            var x = item.position.x;
            item.element.style.zIndex = colXs[x];
        });
    }

    var getScrollbarWidth = (function () {
        var width;
        return function () {
            if (width === undefined) {
                var outerDiv = d.createElement('div');
                var innerDiv = d.createElement('div');
                outerDiv.style.cssText = 'overflow:auto;' +
                    'position:absolute;top:-9999px;width:100px;height:100px';
                innerDiv.style.cssText = 'width:100%;height:200px';
                outerDiv.appendChild(innerDiv);
                d.body.appendChild(outerDiv);
                width = outerDiv.offsetWidth - outerDiv.clientWidth;
                d.body.removeChild(outerDiv);
            }
            return width;
        };
    })();

    // Returns a function that will toggle the vertical scrollbar on and off
    // for `el` based on the difference between it's `offsetHeight` and
    // `scrollHeight` properties. By design, the returned function does
    // nothing unless the result of this comparison has changed.
    function toggleScrollYFn(el) {
        var scrollbarWidth = getScrollbarWidth();
        var computedStyle = w.getComputedStyle(el);
        // parse computed value of paddingRight
        var paddingRightBase = Math.max(
            parseInt(computedStyle.paddingRight, 10),
            scrollbarWidth
        );
        // pre-compute the value that will be used for `paddingRight`
        var paddingRightNoScrollY = paddingRightBase + 'px';
        var paddingRightScrollY = (paddingRightBase - scrollbarWidth) + 'px';
        // Use `overflow-y` if supported, otherwise fallback to `overflow`
        var overflowProp = ('overflowY' in computedStyle) ? 'overflowY' : 'overflow';
        // track scrollbar state internally
        var hasScrollY;

        return function () {
            var init = (hasScrollY === undefined);
            var shouldScrollY = (el.scrollHeight - el.offsetHeight) > 0;
            if ( (init || !hasScrollY) && shouldScrollY ) {
                el.style[overflowProp] = 'scroll';
                el.style.paddingRight = paddingRightScrollY;
                hasScrollY = true;
            } else if ( (init || hasScrollY) && !shouldScrollY ) {
                el.style[overflowProp] = 'hidden';
                el.style.paddingRight = paddingRightNoScrollY;
                hasScrollY = false;
            }
            return hasScrollY;
        };
    }

    function masonryScrollbarFix(msnry) {
        var scrollParent = d.getElementsByTagName('main')[0];
        var toggleScrollY = debounce( toggleScrollYFn(scrollParent), 250 );

        // listen for resize events which may or may not trigger a masonry layout
        w.addEventListener('resize', toggleScrollY);

        // listen for `transitionend` events affecting on the masonry element's height
        msnry.element.addEventListener(transEndEventName, function (evt) {
            if (evt.target === msnry.element && evt.propertyName === "height") {
                toggleScrollY(evt);
            }
        });

        // init
        toggleScrollY();
    }

    function masonryInit() {
        var msnry;

        addClass(docElem, 'scrollbarfix');
        d.addEventListener("DOMContentLoaded", function() {
            var small = d.querySelector('.previews');
            var large = small.cloneNode(true);
            // hide when large layout is active
            small.className += ' hidden--large';
            // hide when small layout is active
            large.className += ' hidden--small';
            small.parentNode.insertBefore(large, small);

            msnry = new Masonry(large, { // options
                itemSelector: '.preview',
                transitionDuration: 0 // disable transitions
            });

            msnry.on('layoutComplete', masonryAddZIndex);

            // do nothing if scrollbar width is zero when present
            if (getScrollbarWidth() > 0) {
                masonryScrollbarFix(msnry);
            } else {
                removeClass(docElem, 'scrollbarfix');
            }

            // trigger masonry layout on refresh
            onRefreshLayout(function () {
                msnry.layout();
            });

            // refresh layout in case of changes
            refreshLayout();
        });
    }

    OPP.masonryInit = masonryInit;
})(window, window.document);

// main
(function (w, d) {
    var OPP = w.OPP;

    // lookup common functions
    var hasClass = OPP.hasClass;
    var masonryInit = OPP.masonryInit;
    var refreshLayout = OPP.refreshLayout;
    var onRefreshLayout = OPP.onRefreshLayout;

    // for repeated calls to `hasClass`
    var docElem = d.documentElement;

    // process ratios and update sizes attribute on refresh
    onRefreshLayout(function () {
        !!w.imageRatio && imageRatio.processImages();
        !!w.lazySizes && lazySizes.autoSizer.checkElems();
    });

    // make sure the aspect ratio is set before being unveiled (good for FF)
    !!w.imageRatio && d.addEventListener('lazybeforeunveil', function (e) {
        imageRatio.processImages([e.target]);
    });

    // run layout and page specific js only if media queries are supported,
    // since we will be using `minimal.css` otherwise.
    if ( Modernizr.mediaquery ) {
        if ( hasClass(docElem, 'l-detailed') && hasClass(docElem, 'p-gallery') ) {
            !!masonryInit && masonryInit();
        }
    }

    // ensure sizes attribute is accurate
    w.addEventListener("load", refreshLayout);
})(window, window.document);
