/*
 * functions - general functions use on all sites
 * Version: 0.2
 */

/*
 * AJAX all forms
 */

window.submitForm =  function submitForm(form) {
    'use strict';

    $(document).on('submit', form, function (e) {
        var form_cache = $(this),
            action = form_cache.attr('action'),
            submit_button = form_cache.find('[type="submit"]');

        var formData = new FormData(form_cache[0]);
        formData.append('ajax', 'true');

        e.preventDefault();

        // stop button from being clickable
        form_cache.addClass('submitting');
        submit_button.attr('disabled', 'disabled');

        // remove any existing instances of .successMessage - prevents multiple success messages
        form_cache.find('.successMessage').remove();

        $.ajax({
            type: 'post',
            url: action,
            data: formData,
            processData: false,
            contentType: false,
            context: form_cache,
            success: function (response) {
                if (typeof response === "string") {
                    response = $.parseJSON(response);
                }
                // cache form within success
                form_cache = $(this);

                submitFormSuccess(response, form_cache, submit_button);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                // remove disabled from button if errors on page
                submit_button.removeAttr('disabled');
            }
        });

    });
};

window.recaptcha_init = function () {
    'use strict';

    let grecaptcha_field = document.querySelectorAll('.g-recaptcha-response');

    grecaptcha.execute(window.recaptcha_key, {
        action:'validate_captcha'
    })
        .then(function(token) {
            // add token value to form
            grecaptcha_field.forEach((el) => {
                el.value = token;
            });
        });
};

window.submitFormSuccess = function submitFormSuccess(response, form_cache, submit_button) {
    var offset;

    // remove traces of errors so form can be updated on a subsequent submit
    form_cache.find('.row').removeClass('errorWrap');
    form_cache.find('.validationMessage').remove();
    form_cache.find('.error').remove();
    form_cache.prev('.success').remove();

    // if errors add these to the dom
    if (response && response.errors !== null && !$.isArray(response.errors)) {

        if ($.type(response.errors) === 'object' && !$.isEmptyObject(response.errors)) {
            // add errors to relevant fields
            $.each(response.errors, function (name, message) {
                // cache fields with errors
                var error_field = form_cache.find('input[name="' + name + '"], textarea[name="' + name + '"], select[name="' + name + '"]'),
                    error_field_last = error_field.last(),
                    error_field_row = error_field.parents('.row');

                // check if error corresponds to an input field
                if (error_field.length > 0) {
                    // added error class to input wrap
                    error_field_row.addClass('errorWrap');

                    error_field.on('keyup change', function () {
                        if (error_field.is(':checkbox') && error_field.is(':checked') || error_field.is(':radio') && error_field.is(':checked') || !error_field.is(':checkbox') && $.trim($(this).val()).length > 0 || !error_field.is(':checkbox') && $.trim($(this).val()) !== '') {
                            error_field_row.removeClass('errorWrap');
                        } else {
                            error_field_row.addClass('errorWrap');
                        }
                    });

                    // add validation message to the dom
                    if (error_field.parents('.buttonInline').length > 0) {
                        error_field.parent('.inputWrap').next('button').after('<div class="validationMessage"><span>' + message + '</span></div>');
                    } else if (error_field.is(':checkbox') || error_field.is(':radio')) {
                        error_field_last.parent('.row').append('<div class="validationMessage"><span>' + message + '</span></div>');
                    } else {
                        error_field.parent('.inputWrap').after('<div class="validationMessage"><span>' + message + '</span></div>');
                    }

                    // add error to dom before form
                } else {

                    // check if ther is an error ul already in the dom - if not add one
                    if (!$('.error ul').length > 0) {
                        form_cache.prepend('<div class="error"><ul></ul></div>');
                    }

                    form_cache.find('.error ul').prepend('<li>' + message + '</li>');

                }

            });

            // remove disabled from button if errors on page
            submit_button.removeAttr('disabled');

            if ($('.overlayBoxOuter').length > 0) {
                $(window).trigger('resize.overlay');
            } else {
                // scroll to first error if the form is not in overlay
                offset = form_cache.find('.errorWrap, .error').eq(0).offset();
                $('html, body').stop(true, true).delay(200).animate({
                    scrollTop: offset.top - 100
                }, 1000);
            }
        }
        // if success then move on
    } else {

        if (response && response.success !== null && !$.isArray(response.success)) {

            if (response.success.redirect) {
                $(location).attr('href', response.success.redirect);
            } else {

                // add a success message div then put all the content in this
                form_cache.before('<div class="successMessage" />');

                // prepend title if exists
                if (response.success.title) {
                    form_cache.prev('.successMessage').append(response.success.title);
                }
                form_cache.prev('.successMessage').append('<div class="message">' + response.success.message + '</div>');

                if (form_cache.find('.removeAfterSubmit').length) {
                    form_cache.find('.removeAfterSubmit').remove();
                }

                if (response.success.hide_form) {
                    form_cache.fadeOut();
                }

                // scroll to success message if the form is not in overlay
                if ($('.overlayBoxOuter').length < 1) {
                    offset = $('.successMessage').offset();
                    $('html, body').stop(true, true).delay(200).animate({
                        scrollTop: offset.top - 100
                    }, 1000);
                }
            }

            // remove disabled from button if errors on page
            form_cache.find('[type="submit"]').removeAttr('disabled');

            if ($('.overlayBoxOuter').length > 0) {
                $(window).trigger('resize.overlay');
            }
        }
    }

    // Reset recaptcha for all forms on the page
    if (typeof grecaptcha !== 'undefined' && window.recaptcha_key !== '' && form_cache.find('.js_g-recaptcha-response').length > 0) {
        recaptcha_init();
    }
};

/*
 * Get breakpoints from css
 */

window.getBreakpoint = function getBreakpoint() {
    'use strict';

    // set some vars
    var style = null,
        breakpoint = [],
        breakpoints = [],
        i;

    /*
     * Use the js function getComputedStyle() to grab the styles of pseudo elements
     */
    style = window.getComputedStyle(document.documentElement, ':before');

    // split breakpoints into array
    style = style.content.replace(/\,\s+/g, ',').split(',');

    // split each breakpoint into array
    $.each(style, function () {
        // remove px from value and split each value into breakpoint name and number
        breakpoint = this.replace(': ', ':').replace('px', '').replace('\'', '').replace('"', '').split(':');

        // build array with breakpoint name as key and number as value
        for (i = 0; i < breakpoint.length; i++) {
            breakpoints[breakpoint[0]] = parseInt(breakpoint[1], 10);
        }
    });

    // return breakpoints
    return breakpoints;
};

/*
 * Debounce
 */

window.debouncer = function debouncer(func, timeout) {
    'use strict';

    var timeoutID;
    timeout = timeout || 200;

    return function () {
        var scope = this,
            args = arguments;

        clearTimeout(timeoutID);

        timeoutID = setTimeout(function () {
            func.apply(scope, Array.prototype.slice.call(args));
        }, timeout);
    };
};

/*
 * Returns whether a given media query is true
 * Params: (0, 800) checks if browser width is 800 or below; (801) or (801,0) checks if browser width is 801 or above
 * Alternatively, one or both arguments can be strings, such as "mobile" or "tablet" (from window.breakpoints)
 * in this case the min_width value will automatically have 1 added to it
 */

window.matchesMediaQuery =  function matchesMediaQuery(min_width, max_width) {
    'use strict';

    // check if matchMedia is supported
    if (!window.matchMedia) {
        return false;
    }

    // If strings any strings are supplied, grab that breakpoint from window.breakpoints
    // e.g. "mobile" grabs window.breakpoints.mobile
    if (typeof min_width === "string") {
        if (window.breakpoints.hasOwnProperty(min_width)) {
            min_width = window.breakpoints[min_width] + 1;
        } else {
            return false;
        }
    }
    if (typeof max_width === "string" ) {
        if (window.breakpoints.hasOwnProperty(max_width)) {
            max_width = window.breakpoints[max_width];
        } else {
            return false;
        }
    }

    // check which arguments are set
    var max_is_set = true,
        min_is_set = true;

    if ((min_width === undefined || min_width === 0)) {
        min_is_set = false;
    }
    if ((max_width === undefined || max_width === 0)) {
        max_is_set = false;
    }

    // perform the relevant media query based on which arguments have been supplied
    if (max_is_set && !min_is_set) {
        return window.matchMedia('(max-width: ' + max_width + 'px)').matches;
    } else if (!max_is_set && min_is_set) {
        return window.matchMedia('(min-width: ' + min_width + 'px)').matches;
    } else if (max_is_set && min_is_set) {
        return window.matchMedia('(min-width: ' + min_width + 'px) and (max-width: ' + max_width + 'px)').matches;
    } else {
        return false;
    }
};

/*
 * Slide Fade Toggle
 */

$.fn.slideFadeToggle  = function (speed, easing, callback) {
    'use strict';

    return this.animate({
        opacity: 'toggle',
        height: 'toggle',
        padding: 'toggle auto',
        margin: 'toggle auto'
    }, speed, easing, callback);
};

/*
 * Scroll Page Function
 * Works on anchors and form elements
 */

$.fn.scrollPage = function (options, callback) {
    'use strict';

    var target = this,
        defaults = {
            // browser check to use appropriate element
            scroll: 'html, body',
            speed: 500,
            easing: 'linear',
            scroll_offset: 0,
            event_handler: 'click'
        },
        settings = $.extend({}, defaults, options);

    return target.on(settings.event_handler, function (e) {
        var $this = $(this),
            href = $this.attr('href');
        // change href to value if event_handler is change
        if (settings.event_handler === 'change') {
            href = $this.val();
        }

        // if the href has a # scroll to act as it if wasn't even here
        if ($(href).length && href.indexOf('#') > - 1) {
            // prevent default only if needed
            e.preventDefault();

            $(settings.scroll)
                .stop(true, true)
                .animate({

                    scrollTop: $(href).offset().top + settings.scroll_offset

                }, settings.speed, settings.easing)
                .promise()
                .then(function () {

                    if (typeof callback === 'function') {
                        callback();
                    }

                });
        }
    });
};

/*
 * ios style switch swipe
 */

window.iosStyleSwitch = function iosStyleSwitch() {
    'use strict';

    $('.js_switch').on('swipeleft', function (e, data) {
        e.preventDefault();
        $(this).prev().prop("checked", false);
    });

    $('.js_switch').on('swiperight', function (e, data) {
        e.preventDefault();
        $(this).prev().prop("checked", true);
    });

};

/*
 * File Upload
 */

window.fileUpload = function fileUpload() {

    // .js_fileUpload = input field
    $('.js_fileUpload').on('change', function () {
        var input_field = $(this),
            files = input_field.get(0).files,
            filename = '',
            filename_field = '.js_fileName';

        // remove previous filename
        input_field.parent().find(filename_field).empty();

        // foreach file within file object
        $(files).each(function () {
            // get filename from object
            filename = $(this).get(0).name;

            // append filename within span to DOM
            input_field.parent().find(filename_field).append('<span>' + filename + '</span>');
        });
    });

}

/*
 * Trigger section below breakpoint. Selector is the trigger
 */

window.showHideMenus = function showHideMenus(selector, breakpoint) {
    'use strict';

    var trigger = $(selector),
        section = trigger.next();

    if (typeof (trigger.data('target')) !== 'undefined') {
        section = $(trigger.data('target'));
    }

    // mobile: show / hide behaviour
    if (matchesMediaQuery(0, breakpoint) && !trigger.hasClass('isClickable')) {

        // initialise show / hide state
        section.hide();
        trigger.addClass('isClickable');
        // bind click
        trigger.on('click', function (e) {

            var this_section = $(this).next(),
                this_trigger = $(this);

            e.preventDefault();

            if (typeof (this_trigger.data('target')) !== 'undefined') {
                this_section = $(this_trigger.data('target'));
            }

            this_section.toggleClass('open').slideFadeToggle();
            this_trigger.toggleClass('open');
        });

    } else if (matchesMediaQuery(Number(breakpoint + 1)) && trigger.hasClass('isClickable')) {
        trigger.removeClass('isClickable');
        // disable click and remove classes
        trigger.removeClass('open').off('click');
        section.removeClass('open').css({
            display: ''
        });
    }

};

$(document).ready(function () {
    'use strict';

/*
 * Set initial window width on document ready
 */

    window.windowWidth = $(window).outerWidth();

/*
 * Set window breakpoints variable
 * call by using window.breakpoints.BREAKPOINT_NAME
 */
    window.breakpoints = getBreakpoint();

}); // document ready

// Check if point in rectangle
window.isPointInRect = function isPointInRect(x, y, rect_w, rect_h) {
    'use strict';

    return (x >= 0 && x <= rect_w) && (y >= 0 && y <= rect_h);
};

// calculate if element is visible in the screen
window.isElementInViewport = function isElementInViewport(el, offset) {
    'use strict';
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect(),
        w_width = window.innerWidth || doc.documentElement.clientWidth,
        w_height = window.innerHeight || doc.documentElement.clientHeight,
        $el = $(el);

    // quick check if element does not exist
    if ($el.length === 0) {
        return false;
    }

    // Here we manually block if element is in viewport before slider is initialized
    if ($el.hasClass('skipVisibilityCheck') || $el.closest('.skipVisibilityCheck').length > 0) {
        return false;
    }

    // If offset is not set then set to 0
    // If offset is not 0, then element is being showed before offset value
    if (typeof(offset) === 'undefined') {
        offset = 0;
    }

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < (0 - offset)
        || rect.left > w_width || rect.top > (w_height + offset)) {
        return false;
    }

    // Return true if any of its four corners are visible
    return isPointInRect(rect.left,  (rect.top - offset), w_width, w_height)
        || isPointInRect(rect.right, (rect.top - offset), w_width, w_height)
        || isPointInRect(rect.right, (rect.bottom + offset), w_width, w_height)
        || isPointInRect(rect.left,  (rect.bottom + offset), w_width, w_height);
};
