var fitnote = fitnote || {};
// Set a default value for scrolled.
fitnote.scrolled = 0;
// polyfill closest
// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
if (!Element.prototype.closest) {
    Element.prototype.closest = function (s) {
        var el = this;
        do {
            if (el.matches(s)) {
                return el;
            }
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === 1);
        return null;
    };
}
// polyfill forEach
// https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = function (callback, thisArg) {
        var i;
        var len = this.length;
        thisArg = thisArg || window;
        for (i = 0; i < len; i++) {
            callback.call(thisArg, this[i], i, this);
        }
    };
}
// event "polyfill"
fitnote.createEvent = function (eventName) {
    var event;
    if (typeof window.Event === 'function') {
        event = new Event(eventName);
    } else {
        event = document.createEvent('Event');
        event.initEvent(eventName, true, false);
    }
    return event;
};
// matches "polyfill"
// https://developer.mozilla.org/es/docs/Web/API/Element/matches
if (!Element.prototype.matches) {
    Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length;
        while (--i >= 0 && matches.item(i) !== this) {
        }
        return i > -1;
    };
}
// Add a class to the body for when touch is enabled for browsers that don't support media queries
// for interaction media features. Adapted from <https://codepen.io/Ferie/pen/vQOMmO>.
fitnote.touchEnabled = {
    init: function () {
        var matchMedia = function () {
            // Include the 'heartz' as a way to have a non-matching MQ to help terminate the join. See <https://git.io/vznFH>.
            var prefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
            var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
        };
        if (('ontouchstart' in window) || (window.DocumentTouch && document instanceof window.DocumentTouch) || matchMedia()) {
            document.body.classList.add('touch-enabled');
        }
    }
}; // fitnote.touchEnabled
/*	-----------------------------------------------------------------------------------------------
	Cover Modals
--------------------------------------------------------------------------------------------------- */
fitnote.coverModals = {
    init: function () {
        if (document.querySelector('.cover-modal')) {
            // Handle cover modals when they're toggled.
            this.onToggle();
            // When toggled, untoggle if visitor clicks on the wrapping element of the modal.
            this.outsideUntoggle();
            // Close on escape key press.
            this.closeOnEscape();
            // Hide and show modals before and after their animations have played out.
            this.hideAndShowModals();
        }
    },
    // Handle cover modals when they're toggled.
    onToggle: function () {
        document.querySelectorAll('.cover-modal').forEach(function (element) {
            element.addEventListener('toggled', function (event) {
                var modal = event.target, body = document.body;
                if (modal.classList.contains('active')) {
                    body.classList.add('showing-modal');
                } else {
                    body.classList.remove('showing-modal');
                    body.classList.add('hiding-modal');
                    // Remove the hiding class after a delay, when animations have been run.
                    setTimeout(function () {
                        body.classList.remove('hiding-modal');
                    }, 500);
                }
            });
        });
    },
    // Close modal on outside click.
    outsideUntoggle: function () {
        document.addEventListener('click', function (event) {
            var target = event.target;
            var modal = document.querySelector('.cover-modal.active');
            // if target onclick is <a> with # within the href attribute
            if (event.target.tagName.toLowerCase() === 'a' && event.target.hash.includes('#') && modal !== null) {
                // untoggle the modal
                this.untoggleModal(modal);
                // wait 550 and scroll to the anchor
                setTimeout(function () {
                    var anchor = document.getElementById(event.target.hash.slice(1));
                    anchor.scrollIntoView();
                }, 550);
            }
            if (target === modal) {
                this.untoggleModal(target);
            }
        }.bind(this));
    },
    // Close modal on escape key press.
    closeOnEscape: function () {
        document.addEventListener('keydown', function (event) {
            if (event.keyCode === 27) {
                event.preventDefault();
                document.querySelectorAll('.cover-modal.active').forEach(function (element) {
                    this.untoggleModal(element);
                }.bind(this));
            }
        }.bind(this));
    },
    // Hide and show modals before and after their animations have played out.
    hideAndShowModals: function () {
        var _doc = document, _win = window, modals = _doc.querySelectorAll('.cover-modal'),
            htmlStyle = _doc.documentElement.style, adminBar = _doc.querySelector('#wpadminbar');

        function getAdminBarHeight(negativeValue) {
            var height, currentScroll = _win.pageYOffset;
            if (adminBar) {
                height = currentScroll + adminBar.getBoundingClientRect().height;
                return negativeValue ? -height : height;
            }
            return currentScroll === 0 ? 0 : -currentScroll;
        }

        function htmlStyles() {
            var overflow = _win.innerHeight > _doc.documentElement.getBoundingClientRect().height;
            return {
                'overflow-y': overflow ? 'hidden' : 'scroll',
                position: 'fixed',
                width: '100%',
                top: getAdminBarHeight(true) + 'px',
                left: 0
            };
        }

        // Show the modal.
        modals.forEach(function (modal) {
            modal.addEventListener('toggle-target-before-inactive', function (event) {
                var styles = htmlStyles(), offsetY = _win.pageYOffset,
                    paddingTop = (Math.abs(getAdminBarHeight()) - offsetY) + 'px',
                    mQuery = _win.matchMedia('(max-width: 600px)');
                if (event.target !== modal) {
                    return;
                }
                Object.keys(styles).forEach(function (styleKey) {
                    htmlStyle.setProperty(styleKey, styles[styleKey]);
                });
                _win.fitnote.scrolled = parseInt(styles.top, 10);
                if (adminBar) {
                    _doc.body.style.setProperty('padding-top', paddingTop);
                    if (mQuery.matches) {
                        if (offsetY >= getAdminBarHeight()) {
                            modal.style.setProperty('top', 0);
                        } else {
                            modal.style.setProperty('top', (getAdminBarHeight() - offsetY) + 'px');
                        }
                    }
                }
                modal.classList.add('show-modal');
            });
            // Hide the modal after a delay, so animations have time to play out.
            modal.addEventListener('toggle-target-after-inactive', function (event) {
                if (event.target !== modal) {
                    return;
                }
                setTimeout(function () {
                    var clickedEl = fitnote.toggles.clickedEl;
                    modal.classList.remove('show-modal');
                    Object.keys(htmlStyles()).forEach(function (styleKey) {
                        htmlStyle.removeProperty(styleKey);
                    });
                    if (adminBar) {
                        _doc.body.style.removeProperty('padding-top');
                        modal.style.removeProperty('top');
                    }
                    if (clickedEl !== false) {
                        clickedEl.focus();
                        clickedEl = false;
                    }
                    _win.scrollTo(0, Math.abs(_win.fitnote.scrolled + getAdminBarHeight()));
                    _win.fitnote.scrolled = 0;
                }, 500);
            });
        });
    },
    // Untoggle a modal.
    untoggleModal: function (modal) {
        var modalTargetClass, modalToggle = false;
        // If the modal has specified the string (ID or class) used by toggles to target it, untoggle the toggles with that target string.
        // The modal-target-string must match the string toggles use to target the modal.
        if (modal.dataset.modalTargetString) {
            modalTargetClass = modal.dataset.modalTargetString;
            modalToggle = document.querySelector('*[data-toggle-target="' + modalTargetClass + '"]');
        }
        // If a modal toggle exists, trigger it so all of the toggle options are included.
        if (modalToggle) {
            modalToggle.click();
            // If one doesn't exist, just hide the modal.
        } else {
            modal.classList.remove('active');
        }
    }
}; // fitnote.coverModals
/*	-----------------------------------------------------------------------------------------------
	Modal Menu
--------------------------------------------------------------------------------------------------- */
fitnote.modalMenu = {
    init: function () {
        // If the current menu item is in a sub level, expand all the levels higher up on load.
        this.expandLevel();
        this.keepFocusInModal();
    },
    expandLevel: function () {
        var modalMenus = document.querySelectorAll('.modal-menu');
        modalMenus.forEach(function (modalMenu) {
            var activeMenuItem = modalMenu.querySelector('.current-menu-item');
            if (activeMenuItem) {
                fitnoteFindParents(activeMenuItem, 'li').forEach(function (element) {
                    var subMenuToggle = element.querySelector('.sub-menu-toggle');
                    if (subMenuToggle) {
                        fitnote.toggles.performToggle(subMenuToggle, true);
                    }
                });
            }
        });
    },
    keepFocusInModal: function () {
        var _doc = document;
        _doc.addEventListener('keydown', function (event) {
            var toggleTarget, modal, selectors, elements, menuType, bottomMenu, activeEl, lastEl, firstEl, tabKey,
                shiftKey, clickedEl = fitnote.toggles.clickedEl;
            if (clickedEl && _doc.body.classList.contains('showing-modal')) {
                toggleTarget = clickedEl.dataset.toggleTarget;
                selectors = 'input, a, button';
                modal = _doc.querySelector(toggleTarget);
                elements = modal.querySelectorAll(selectors);
                elements = Array.prototype.slice.call(elements);
                if ('.menu-modal' === toggleTarget) {

                    menuType = '.mobile-menu';
                    elements = elements.filter(function (element) {
                        return null !== element.closest(menuType) && null !== element.offsetParent;
                    });
                    elements.unshift(_doc.querySelector('.close-nav-toggle'));
                    bottomMenu = _doc.querySelector('.menu-bottom > nav');
                    if (bottomMenu) {
                        bottomMenu.querySelectorAll(selectors).forEach(function (element) {
                            elements.push(element);
                        });
                    }
                }
                lastEl = elements[elements.length - 1];
                firstEl = elements[0];
                activeEl = _doc.activeElement;
                tabKey = event.keyCode === 9;
                shiftKey = event.shiftKey;
                if (!shiftKey && tabKey && lastEl === activeEl) {
                    event.preventDefault();
                    firstEl.focus();
                }
                if (shiftKey && tabKey && firstEl === activeEl) {
                    event.preventDefault();
                    lastEl.focus();
                }
            }
        });
    }
}; // fitnote.modalMenu
/*	-----------------------------------------------------------------------------------------------
	Primary Menu
--------------------------------------------------------------------------------------------------- */
fitnote.primaryMenu = {
    init: function () {
        this.focusMenuWithChildren();
    },
    // The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
    // by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
    focusMenuWithChildren: function () {
        // Get all the link elements within the primary menu.
        var links, i, len, menu = document.querySelector('.primary-menu-wrapper');
        if (!menu) {
            return false;
        }
        links = menu.getElementsByTagName('a');
        // Each time a menu link is focused or blurred, toggle focus.
        for (i = 0, len = links.length; i < len; i++) {
            links[i].addEventListener('focus', toggleFocus, true);
            links[i].addEventListener('blur', toggleFocus, true);
        }

        //Sets or removes the .focus class on an element.
        function toggleFocus() {
            var self = this;
            // Move up through the ancestors of the current link until we hit .primary-menu.
            while (-1 === self.className.indexOf('primary-menu')) {
                // On li elements toggle the class .focus.
                if ('li' === self.tagName.toLowerCase()) {
                    if (-1 !== self.className.indexOf('focus')) {
                        self.className = self.className.replace(' focus', '');
                    } else {
                        self.className += ' focus';
                    }
                }
                self = self.parentElement;
            }
        }
    }
}; // fitnote.primaryMenu
/*	-----------------------------------------------------------------------------------------------
	Toggles
--------------------------------------------------------------------------------------------------- */
fitnote.toggles = {
    clickedEl: false,
    init: function () {
        // Do the toggle.
        this.toggle();
        // Check for toggle/untoggle on resize.
        this.resizeCheck();
        // Check for untoggle on escape key press.
        this.untoggleOnEscapeKeyPress();
    },
    performToggle: function (element, instantly) {
        var target, timeOutTime, classToToggle, self = this, _doc = document, // Get our targets.
            toggle = element, targetString = toggle.dataset.toggleTarget, activeClass = 'active';
        // Elements to focus after modals are closed.
        if (!_doc.querySelectorAll('.show-modal').length) {
            self.clickedEl = _doc.activeElement;
        }
        if (targetString === 'next') {
            target = toggle.nextSibling;
        } else {
            target = _doc.querySelector(targetString);
        }
        // Trigger events on the toggle targets before they are toggled.
        if (target.classList.contains(activeClass)) {
            target.dispatchEvent(fitnote.createEvent('toggle-target-before-active'));
        } else {
            target.dispatchEvent(fitnote.createEvent('toggle-target-before-inactive'));
        }
        // Get the class to toggle, if specified.
        classToToggle = toggle.dataset.classToToggle ? toggle.dataset.classToToggle : activeClass;
        // For cover modals, set a short timeout duration so the class animations have time to play out.
        timeOutTime = 0;
        if (target.classList.contains('cover-modal')) {
            timeOutTime = 10;
        }
        setTimeout(function () {
            var focusElement, subMenued = target.classList.contains('sub-menu'),
                newTarget = subMenued ? toggle.closest('.menu-item').querySelector('.sub-menu') : target,
                duration = toggle.dataset.toggleDuration;
            // Toggle the target of the clicked toggle.
            if (toggle.dataset.toggleType === 'slidetoggle' && !instantly && duration !== '0') {
                fitnoteMenuToggle(newTarget, duration);
            } else {
                newTarget.classList.toggle(classToToggle);
            }
            // If the toggle target is 'next', only give the clicked toggle the active class.
            if (targetString === 'next') {
                toggle.classList.toggle(activeClass);
            } else if (target.classList.contains('sub-menu')) {
                toggle.classList.toggle(activeClass);
            } else {
                // If not, toggle all toggles with this toggle target.
                _doc.querySelector('*[data-toggle-target="' + targetString + '"]').classList.toggle(activeClass);
            }
            // Toggle aria-expanded on the toggle.
            fitnoteToggleAttribute(toggle, 'aria-expanded', 'true', 'false');
            if (self.clickedEl && -1 !== toggle.getAttribute('class').indexOf('close-')) {
                fitnoteToggleAttribute(self.clickedEl, 'aria-expanded', 'true', 'false');
            }
            // Toggle body class.
            if (toggle.dataset.toggleBodyClass) {
                _doc.body.classList.toggle(toggle.dataset.toggleBodyClass);
            }
            // Check whether to set focus.
            if (toggle.dataset.setFocus) {
                focusElement = _doc.querySelector(toggle.dataset.setFocus);
                if (focusElement) {
                    if (target.classList.contains(activeClass)) {
                        focusElement.focus();
                    } else {
                        focusElement.blur();
                    }
                }
            }
            // Trigger the toggled event on the toggle target.
            target.dispatchEvent(fitnote.createEvent('toggled'));
            // Trigger events on the toggle targets after they are toggled.
            if (target.classList.contains(activeClass)) {
                target.dispatchEvent(fitnote.createEvent('toggle-target-after-active'));
            } else {
                target.dispatchEvent(fitnote.createEvent('toggle-target-after-inactive'));
            }
        }, timeOutTime);
    },
    // Do the toggle.
    toggle: function () {
        var self = this;
        document.querySelectorAll('*[data-toggle-target]').forEach(function (element) {
            element.addEventListener('click', function (event) {
                event.preventDefault();
                self.performToggle(element);
            });
        });
    },
    // Check for toggle/untoggle on screen resize.
    resizeCheck: function () {
        if (document.querySelectorAll('*[data-untoggle-above], *[data-untoggle-below], *[data-toggle-above], *[data-toggle-below]').length) {
            window.addEventListener('resize', function () {
                var winWidth = window.innerWidth, toggles = document.querySelectorAll('.toggle');
                toggles.forEach(function (toggle) {
                    var unToggleAbove = toggle.dataset.untoggleAbove, unToggleBelow = toggle.dataset.untoggleBelow,
                        toggleAbove = toggle.dataset.toggleAbove, toggleBelow = toggle.dataset.toggleBelow;
                    // If no width comparison is set, continue.
                    if (!unToggleAbove && !unToggleBelow && !toggleAbove && !toggleBelow) {
                        return;
                    }
                    // If the toggle width comparison is true, toggle the toggle.
                    if ((((unToggleAbove && winWidth > unToggleAbove) || (unToggleBelow && winWidth < unToggleBelow)) && toggle.classList.contains('active')) || (((toggleAbove && winWidth > toggleAbove) || (toggleBelow && winWidth < toggleBelow)) && !toggle.classList.contains('active'))) {
                        toggle.click();
                    }
                });
            });
        }
    },
    // Close toggle on escape key press.
    untoggleOnEscapeKeyPress: function () {
        document.addEventListener('keyup', function (event) {
            if (event.key === 'Escape') {
                document.querySelectorAll('*[data-untoggle-on-escape].active').forEach(function (element) {
                    if (element.classList.contains('active')) {
                        element.click();
                    }
                });
            }
        });
    }
}; // fitnote.toggles
/**
 * Is the DOM ready?
 *
 * This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
 *
 * @since FitNote 1.0.0
 *
 * @param {Function} fn Callback function to run.
 */
function fitnoteDomReady(fn) {
    if (typeof fn !== 'function') {
        return;
    }
    if (document.readyState === 'interactive' || document.readyState === 'complete') {
        return fn();
    }
    document.addEventListener('DOMContentLoaded', fn, false);
}

fitnoteDomReady(function () {
    fitnote.toggles.init();              // Handle toggles.
    fitnote.coverModals.init();          // Handle cover modals.
    fitnote.modalMenu.init();            // Modal Menu.
    fitnote.primaryMenu.init();          // Primary Menu.
    fitnote.touchEnabled.init();         // Add class to body if device is touch-enabled.
});
/*	-----------------------------------------------------------------------------------------------
	Helper functions
--------------------------------------------------------------------------------------------------- */

/* Toggle an attribute ----------------------- */
function fitnoteToggleAttribute(element, attribute, trueVal, falseVal) {
    var toggles;
    if (!element.hasAttribute(attribute)) {
        return;
    }
    if (trueVal === undefined) {
        trueVal = true;
    }
    if (falseVal === undefined) {
        falseVal = false;
    }
    /*
     * Take into account multiple toggle elements that need their state to be
     * synced. For example: the Search toggle buttons for desktop and mobile.
     */
    toggles = document.querySelectorAll('[data-toggle-target="' + element.dataset.toggleTarget + '"]');
    toggles.forEach(function (toggle) {
        if (!toggle.hasAttribute(attribute)) {
            return;
        }
        if (toggle.getAttribute(attribute) !== trueVal) {
            toggle.setAttribute(attribute, trueVal);
        } else {
            toggle.setAttribute(attribute, falseVal);
        }
    });
}

/**
 * Toggle a menu item on or off.
 *
 * @since FitNote 1.0.0
 *
 * @param {HTMLElement} target
 * @param {number} duration
 */
function fitnoteMenuToggle(target, duration) {
    var initialParentHeight, finalParentHeight, menu, menuItems, transitionListener, initialPositions = [],
        finalPositions = [];
    if (!target) {
        return;
    }
    menu = target.closest('.menu-wrapper');
    // Step 1: look at the initial positions of every menu item.
    menuItems = menu.querySelectorAll('.menu-item');
    menuItems.forEach(function (menuItem, index) {
        initialPositions[index] = {x: menuItem.offsetLeft, y: menuItem.offsetTop};
    });
    initialParentHeight = target.parentElement.offsetHeight;
    target.classList.add('toggling-target');
    // Step 2: toggle target menu item and look at the final positions of every menu item.
    target.classList.toggle('active');
    menuItems.forEach(function (menuItem, index) {
        finalPositions[index] = {x: menuItem.offsetLeft, y: menuItem.offsetTop};
    });
    finalParentHeight = target.parentElement.offsetHeight;
    // Step 3: close target menu item again.
    // The whole process happens without giving the browser a chance to render, so it's invisible.
    target.classList.toggle('active');
    /*
     * Step 4: prepare animation.
     * Position all the items with absolute offsets, at the same starting position.
     * Shouldn't result in any visual changes if done right.
     */
    menu.classList.add('is-toggling');
    target.classList.toggle('active');
    menuItems.forEach(function (menuItem, index) {
        var initialPosition = initialPositions[index];
        if (initialPosition.y === 0 && menuItem.parentElement === target) {
            initialPosition.y = initialParentHeight;
        }
        menuItem.style.transform = 'translate(' + initialPosition.x + 'px, ' + initialPosition.y + 'px)';
    });
    /*
     * The double rAF is unfortunately needed, since we're toggling CSS classes, and
     * the only way to ensure layout completion here across browsers is to wait twice.
     * This just delays the start of the animation by 2 frames and is thus not an issue.
     */
    requestAnimationFrame(function () {
        requestAnimationFrame(function () {
            /*
             * Step 5: start animation by moving everything to final position.
             * All the layout work has already happened, while we were preparing for the animation.
             * The animation now runs entirely in CSS, using cheap CSS properties (opacity and transform)
             * that don't trigger the layout or paint stages.
             */
            menu.classList.add('is-animating');
            menuItems.forEach(function (menuItem, index) {
                var finalPosition = finalPositions[index];
                if (finalPosition.y === 0 && menuItem.parentElement === target) {
                    finalPosition.y = finalParentHeight;
                }
                if (duration !== undefined) {
                    menuItem.style.transitionDuration = duration + 'ms';
                }
                menuItem.style.transform = 'translate(' + finalPosition.x + 'px, ' + finalPosition.y + 'px)';
            });
            if (duration !== undefined) {
                target.style.transitionDuration = duration + 'ms';
            }
        });
        // Step 6: finish toggling.
        // Remove all transient classes when the animation ends.
        transitionListener = function () {
            menu.classList.remove('is-animating');
            menu.classList.remove('is-toggling');
            target.classList.remove('toggling-target');
            menuItems.forEach(function (menuItem) {
                menuItem.style.transform = '';
                menuItem.style.transitionDuration = '';
            });
            target.style.transitionDuration = '';
            target.removeEventListener('transitionend', transitionListener);
        };
        target.addEventListener('transitionend', transitionListener);
    });
}

/**
 * Traverses the DOM up to find elements matching the query.
 *
 * @since FitNote 1.0.0
 *
 * @param {HTMLElement} target
 * @param {string} query
 * @return {NodeList} parents matching query
 */
function fitnoteFindParents(target, query) {
    var parents = [];

    // Recursively go up the DOM adding matches to the parents array.
    function traverse(item) {
        var parent = item.parentNode;
        if (parent instanceof HTMLElement) {
            if (parent.matches(query)) {
                parents.push(parent);
            }
            traverse(parent);
        }
    }

    traverse(target);
    return parents;
}
