var flex = (typeof flex !== 'undefined') ? flex : {};

flex.xhrAbort = function (xhr) {
    !xhr || xhr.abort();
};

flex.getUrl = function (url) {
    var x = /^https?:/;
    url = url.trim();
    url = url.trim('/');
    if (x.test(url)) {
        return url;
    }

    return flexconfig.baseUrl + url;
};

flex.getUrlCdn = function (url) {
    var x = /^https?:/;
    url = url.trim();
    url = url.trim('/');
    if (x.test(url)) {
        return url;
    }

    return flexconfig.baseUrlCdn + url;
};

flex.get = function (url, data, success, dataType) {
    flex.ajax({
        type: "GET",
        url: this.getUrl(url),
        data: data,
        success: success,
        dataType: dataType
    });
};

flex.getJson = function (url, data, success) {
    this.get(url, data, success, 'json');
};

flex.getHtml = function (url, data, success) {
    this.get(url, data, success, 'html');
};

flex.post = function (url, data, success, dataType) {
    dataType = (typeof dataType !== 'undefined') ? dataType : 'json';
    flex.ajax({
        type: "POST",
        url: this.getUrl(url),
        data: data,
        success: success,
        dataType: dataType
    });
};

flex.ajax = function (data) {
    var x = 'xhr_' + data.url;
    this.xhrAbort(window[x]);

    window[x] = $.ajax(data);
};

// https://stackoverflow.com/a/18358056/497368
flex.roundToPrecision = function (number, precision) {
    // prevent negative numbers from rounding to 0
    var isNegative = number < 0;
    if (isNegative) {
        number = number * -1;
    }
    number = +(Math.round(number + "e+"+ precision) + "e-" + precision);
    if (isNegative) {
        number = number * -1;
    }
    return number;
};

flex.currencyMap = function(code) {
    var item, i;
    for (i=0;i<flexconfig.currencies.length;i++) {
        item = flexconfig.currencies[i];

        if (item.code == code) {
            return item;
        }
    }
};

flex.formatMoney = function (v, code, currencyDisplay, precision) {
    var defaultCode = flexconfig.currency_code;
    code = (typeof code !== 'undefined') ? code : defaultCode;
    currencyDisplay = typeof currencyDisplay !== 'undefined' ? currencyDisplay : flexconfig.currency_display;

    v = parseFloat(v);

    var currency = flex.currencyMap(code);

    // look for default if not found
    if (!currency) {
        code = defaultCode;
        currency = flex.currencyMap(code);
    }

    precision = typeof precision !== 'undefined' && precision ? precision : currency.precision;
    var thousand = currency.thousand_separator,
            decimal = currency.decimal_separator,
            swapSymbol = currency.swap_currency_symbol,
            symbol = currency.symbol;

    v = accounting.formatMoney(v, '', precision, thousand, decimal);

    if (!currencyDisplay) {
        return v;
    } else if (currencyDisplay == flexconfig.CURRENCY_DISPLAY_CODE || ! symbol) {
        return v + ' ' + code;
    } else if (swapSymbol) {
        return v + ' ' + symbol.trim();
    } else {
        return symbol + v;
    }
};

flex.formatMoneyFrontend = function (v, code, currencyDisplay, precision) {
    var defaultCode = flexconfig.currency_code;
    code = (typeof code !== 'undefined') ? code : defaultCode;
    currencyDisplay = typeof currencyDisplay !== 'undefined' ? currencyDisplay : flexconfig.currency_display;

    v = parseFloat(v);

    var currency = flex.currencyMap(code);

    // look for default if not found
    if (!currency) {
        code = defaultCode;
        currency = flex.currencyMap(code);
    }

    precision = typeof precision !== 'undefined' && precision ? precision : currency.precision;

    if (((v - parseInt(v)) <= 0) && flexconfig.hide_zero_cents_on_product_prices) {
        precision = 0;
    }

    var thousand = currency.thousand_separator,
        decimal = currency.decimal_separator,
        swapSymbol = currency.swap_currency_symbol,
        symbol = currency.symbol;

    v = accounting.formatMoney(v, '', precision, thousand, decimal);

    if (!currencyDisplay) {
        return v;
    } else if (currencyDisplay == flexconfig.CURRENCY_DISPLAY_CODE || ! symbol) {
        return v + ' ' + code;
    } else if (swapSymbol) {
        return v + ' ' + symbol.trim();
    } else {
        return symbol + v;
    }
};

flex.formatMoneyPrecision = function (v, code, currencyDisplay) {
    return flex.formatMoney(v, code, currencyDisplay, flex.useBestPrecision(v));
};

flex.formatNumber = function (v, code) {
    var defaultCode = flexconfig.currency_code;
    code = (typeof code !== 'undefined') ? code : defaultCode;

    v = parseFloat(v);

    var currency = flex.currencyMap(code);

    // look for default if not found
    if (!currency) {
        code = defaultCode;
        currency = flex.currencyMap(code);
    }

    var precision = currency.precision,
            thousand = currency.thousand_separator,
            decimal = currency.decimal_separator;

    v = accounting.formatMoney(v, '', precision, thousand, decimal);

    return v;
};

flex.formatNumberPlain = function (v, code, precision) {
    var defaultCode = flexconfig.currency_code;
    code = (typeof code !== 'undefined') && code ? code : defaultCode;

    v = parseFloat(v);

    var currency = flex.currencyMap(code);

    // look for default if not found
    if (!currency) {
        code = defaultCode;
        currency = flex.currencyMap(code);
    }

    precision = typeof precision !== 'undefined' && precision ? precision : currency.precision,
            decimal = currency.decimal_separator;

    v = accounting.formatMoney(v, '', precision, '', decimal);

    return v;
};

/**
 * format number plain based on dynamic precision
 * @param v
 * @param code
 * @returns {*}
 */
flex.formatNumberPlainPrecision = function(v, code) {
    return flex.formatNumberPlain(v, code, flex.useBestPrecision(v));
};

flex.useBestPrecision = function(n) {
    var precision = flex.getNumberPrecision(n);

    precision = precision < 2 ? 2 : precision;
    precision = precision > 3 ? 3 : precision;

    return precision;
};

/**
 * Get the number of precision from the number
 *
 * @param n
 * @returns {number}
 */
flex.getNumberPrecision = function(n) {
    var precision, m, c;

    if (!n)
    {
        n = '';
    }

    m = n.toString().match(/\.(\d+)$/);
    c = m ? m[1].toString().replace(/0+$/, '') : '';
    precision = c.length;

    return precision;
};

flex.formatMoneyAbbr = function (v, code, currencyDisplay) {
    var defaultCode = flexconfig.currency_code;
    code = (typeof code !== 'undefined') ? code : defaultCode;
    currencyDisplay = typeof currencyDisplay !== 'undefined' ? currencyDisplay : flexconfig.currency_display;

    v = parseFloat(v);

    var currency = flex.currencyMap(code);

    // look for default if not found
    if (!currency) {
        code = defaultCode;
        currency = flex.currencyMap(code);
    }

    var precision = 0,
        thousand = currency.thousand_separator,
        decimal = currency.decimal_separator,
        swapSymbol = currency.swap_currency_symbol,
        symbol = currency.symbol;

    v = accounting.formatMoney(v, '', precision, '', decimal);

    if (!currencyDisplay) {
        return flex.abbreviateNumber(v, precision);
    } else if (currencyDisplay == flexconfig.CURRENCY_DISPLAY_CODE || ! symbol) {
        return flex.abbreviateNumber(v, precision) + ' ' + code;
    } else if (swapSymbol) {
        return flex.abbreviateNumber(v, precision) + ' ' + symbol.trim();
    } else {
        return symbol + flex.abbreviateNumber(v, precision);
    }
};

flex.toTimeString = function(time) {
    if (!time) {
        return '';
    }

    // firefox throws warning on with this moment
    var timeFormat = 'HH:mm';
    if (/am|pm/.test(time)) {
        timeFormat = 'hh:mm a';
    }

    var mom = moment('1990-01-01 '+time, 'YYYY-MM-DD '+timeFormat);

    if ((flexconfig.TIME_FORMAT_TYPE_24 == flexconfig.time_format_type)) {
        return mom.format('HH:mm');
    } else {
        return mom.format('hh:mm a');
    }
};

flex.xhrAbort = function (xhr) {
    !xhr || xhr.abort();
};

flex.getUrl = function (url) {
    var x = /^https?:/;
    url = url.trim();
    url = url.trim('/');
    if (x.test(url)) {
        return url;
    }

    return flexconfig.baseUrl + url;
};

flex.get = function (url, data, success, dataType) {
    return flex.ajax({
        type: "GET",
        url: this.getUrl(url),
        data: data,
        success: success,
        dataType: dataType
    });
};

flex.getJson = function (url, data, success) {
    return this.get(url, data, success, 'json');
};

flex.getHtml = function (url, data, success) {
    return this.get(url, data, success, 'html');
};

flex.post = function (url, data, success, dataType) {
    dataType = (typeof dataType !== 'undefined') ? dataType : 'json';
    return flex.ajax({
        type: "POST",
        url: this.getUrl(url),
        data: data,
        success: success,
        dataType: dataType
    });
};

flex.ajax = function (data) {
    var x = 'xhr_' + data.url;
    this.xhrAbort(window[x]);

    return window[x] = $.ajax(data);
};

flex.loading = function($container, isLoading) {
    isLoading = (typeof isLoading !== 'undefined') ? isLoading : true;

    if (isLoading) {
        $container.addClass('loading');
    } else {
        $container.removeClass('loading');
    }
};

flex.abbreviateNumber = function (value, precision) {
    var newValue = value;
    var suffixes = ["", "K", "M", "B","T"];
    var suffixNum = 0;
    while (newValue >= 1000) {
        newValue /= 1000;
        suffixNum++;
    }

    newValue = parseInt(newValue);
    newValue = accounting.toFixed(newValue, typeof precision !== 'undefined' ? precision : 2);

    newValue += suffixes[suffixNum];
    return newValue;
};

flex.fullPageLoader = {
    element: function () {
        return $('.js-full-page-loader');
    },

    show: function showFullPageLoader(msg, opacity) {
        var $full = this.element(),
            $msg = $full.find('.full-page-message');

        opacity = typeof opacity !== 'undefined' ? opacity : '.8';

        $full.css({
            'opacity': opacity
        });

        $msg.text('');
        if (msg) {
            $msg.text(msg);
        }
        $full.show();
    },
    hide: function () {
        this.element().hide();
    }
};

flex.confirmModal = function(params) {

    params = jQuery.extend({
        title: i18n.gettext('Message'),
        message: i18n.gettext('Confirm'),
        yesText: i18n.gettext('Yes'),
        noText: i18n.gettext('No'),
        // additional modal class
        classModal: '',
        noCallback: function() {},
        yesCallback: function() {},
    }, params);

    // make sure that we clone and remove the class popup identifier
    var $modal = $('.js-popup-confirm').clone().removeClass('js-popup-confirm');
    $('body').append($modal);
    $modal.addClass(params.classModal);

    $modal.find('.modal-title').html(params.title);
    $modal.find('.modal-message').html(params.message);
    $modal.find('.extra-section').html('');

    // it uses same modal from old confirmpopup, so we need to refresh the links
    $modal.find('.modal-yes').attr('href', '');
    $modal.find('.modal-no').attr('href', '');

    var $modalYes = $modal.find('.modal-yes').text(params.yesText);
    var $modalNo = $modal.find('.modal-no').text(params.noText);

    if (!params.yesText) {
        $modalYes.hide();
    } else {
        $modalYes.show();
    }
    if (!params.noText) {
        $modalNo.hide();
    } else {
        $modalNo.show();
    }
    var yesClicked = false;
    var noClicked = false;
    $modal.one('click.select_event', '.modal-yes', function(e) {
        e.preventDefault();
        params.yesCallback();

        $modalNo.off('click.select_event');
        yesClicked = true;
        $modal.modal('hide');
    });

    $modal.one('click.select_event_no', '.modal-no', function(e) {
        e.preventDefault();
        noClicked = true;
    });

    $modal.one('hide.bs.modal', function() {
        if (!yesClicked) {
            params.noCallback(noClicked);
        }

        $modalYes.off('click.select_event');
        $modalYes.off('click.select_event_no');
    });
    $modal.modal('show');
};

flex.utils = {};
flex.utils.isEmpty = function (val) {
    if (!val || typeof val === 'undefined') {
        return true;
    }

    if (typeof val === 'object') {
        for(var key in val) {
            if(val.hasOwnProperty(key))
                return false;
        }
    } else {
        return false;
    }

    return true;
};

flex.utils.e = function(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
};

flex.utils.uniqueId = function() {
    return Math.floor(Math.random() * 26) + Date.now();
};

flex.utils.getQueryStr = function(name, a) {
    a = typeof a !== 'undefined' ? a : window.location.search;

    var strs = this.parseQueryStr(a);

    if (typeof name === 'undefined') {
        return strs;
    }

    return strs[name];
};

flex.utils.parseQueryStr = function(a) {

    if (a && a.length && a[0] == '?')
    {
        a = a.substr(1);
    }

    var a = a.split('&')
    if (a == "") return {};
    var b = {};
    for (var i = 0; i < a.length; ++i)
    {
        var p=a[i].split('=', 2);
        if (p.length == 1)
            b[p[0]] = "";
        else
            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
    }
    return b;
};

flex.utils.queryStrRemovePart = function(a, part) {
    var r = flex.utils.parseQueryStr(a);

    if (part in r)
    {
        delete r[part];
    }

    return $.param(r);
};

flex.utils.trim = function (v) {
    if (typeof v === 'string') {
        return v.trim();
    }

    return v;
};

flex.utils.massConvert = function (from, to, v) {
    if (!from || !to) {
        return v;
    }

    var fromGrams = flexconfig.measurements.mass[from].grams * v;
    return fromGrams/flexconfig.measurements.mass[to].grams;
};

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
(function() {
    var timeout = {};

    flex.utils.debounce = function (key, func, wait, immediate) {
        wait = typeof wait !== 'undefined' ? wait : 400;
        var context = this, args = arguments;
        var later = function() {
            timeout[key] = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout[key];
        clearTimeout(timeout[key]);
        timeout[key] = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };

    var debounce2 = {};
    flex.utils.debounce2 = function (key, func, wait, immediate) {
        if (typeof debounce2[key] === 'undefined') {
            debounce2[key] = _.debounce(func, wait, immediate);
        }

        debounce2[key]();
    };

    var throttle2 = {};
    flex.utils.throttle2 = function (key, func, wait, immediate) {
        if (typeof throttle2[key] === 'undefined') {
            throttle2[key] = _.throttle(func, wait, immediate);
        }

        throttle2[key]();
    };
})();

// this is the registration of plugins
// that needs to be run for the new html inserted in the page
flex.globalPlugin = {
    registry: [],

    register: function(callback) {
        this.registry.push(callback);
    },

    run: function($parent) {
        $parent = typeof $parent !== 'undefined' ? $parent : $('body');

        var callable;
        for (var x in this.registry) {
            callable = this.registry[x];
            callable($parent);
        }
    }
};

// backward compatibility
flex.initPlugin = function ($parent) {
    flex.globalPlugin.run($parent);
};

flex.globalPopup = function (options) {
    var self = this;

    self.options = jQuery.extend({
        url: '',
        action: 'POST',
        modalSize: '',
        title: '',
        body: '',
    }, options);

    self.$modalContainer = self.cloneModal();

    this.actionListener();
    self.show();
};

flex.globalPopup.prototype = {

    actionListener: function() {
        var self = this;

        // if tries to close the modal and are you sure js adds class dirty, then lets ask for confirmation
        // for the user if he wants to leave, if not then do keep the modal open
        this.$modalContainer.on('hide.bs.modal', function () {
            if (self.getElModalContent().hasClass('loading')) {
                alert(i18n.gettext('Unable to close modal. Please wait until the process is complete.'));
                return false;
            }

            if (self.getElForm().hasClass('dirty')) {
                if (!confirm(i18n.gettext('Are you sure you want to exit without saving your changes?'))) {
                    return false;
                }
            }

            !self.xhr || self.xhr.abort();
        });

        // remove the cloned modal container
        this.$modalContainer.on('hidden.bs.modal', function () {
            self.$modalContainer.remove();
        });
    },
    cloneModal: function() {
        var $orig = $('#globalPopup'),
            $cloned = $orig.clone();

        $cloned
            .attr('id', $orig.attr('id')+'-'+flex.utils.uniqueId())
            .insertAfter($orig);

        return $cloned;
    },
    show: function() {
        var self = this;

        var $title = self.$modalContainer.find('.modal-title'),
            $titleInfo = self.$modalContainer.find('.modal-title-info'),
            $titleActions = self.$modalContainer.find('.modal-title-actions'),
            $body = self.$modalContainer.find('.modal-body');

        $title.text(self.options.title);
        $titleInfo.html('');
        $titleActions.html('');
        $body.html('');

        self.$modalContainer.find('.modal-dialog').attr('class', 'modal-dialog ' + self.options.modalSize);

        if (self.options.url) {
            flex.fullPageLoader.show();

            flex.getHtml(self.options.url, [], function (data) {
                flex.fullPageLoader.hide();
                self.$modalContainer.modal('show');

                $title.html(self.options.title);
                $body.html(data);

                flex.globalPlugin.run($body);

                $body.ready(function(){
                    setTimeout(function() {
                        if (self.options.action == 'validate-form-on-open') {
                            if ($body.find('form').length) {
                                $body.find('form').parsley().validate();
                            }
                        }
                    }, 900);
                });
            });
        } else if (self.options.body) {
            $body.html(self.options.body);

            self.$modalContainer.modal('show');
        }
    },

    getModalContainer: function () {
        return this.$modalContainer;
    },

    getElForm: function () {
        return this.getElModalContent().find('form');
    },

    getElModalContent: function() {
        return this.getModalContainer().find('.modal-content');
    },
}

flex.initPhotoSwipeFromDOM = function (gallerySelector) {

    var parseThumbnailElements = function (el) {
        var thumbElements = $('figure'),
                numNodes = thumbElements.length,
                items = [],
                figureEl,
                linkEl,
                size,
                item;

        for (var i = 0; i < numNodes; i++) {

            figureEl = thumbElements[i]; // <figure> element

            // include only element nodes
            if (figureEl.nodeType !== 1) {
                continue;
            }

            linkEl = figureEl.children[0]; // <a> element

            size = linkEl.getAttribute('data-size').split('x');

            // create slide object
            var href = linkEl.getAttribute('data-href');
            if (!href) {
                href = linkEl.getAttribute('href');
            }

            item = {
                src: href,
                w: parseInt(size[0], 10),
                h: parseInt(size[1], 10)
            };


            if (figureEl.children.length > 1) {
                // <figcaption> content
                item.title = figureEl.children[1].innerHTML;
            }

            if (linkEl.children.length > 0) {
                // <img> thumbnail element, retrieving thumbnail url
                item.msrc = linkEl.children[0].getAttribute('src');
            }

            item.el = figureEl; // save link to element for getThumbBoundsFn
            items.push(item);
        }

        return items;
    };

    // find nearest parent element
    var closest = function closest(el, fn) {
        return el && (fn(el) ? el : closest(el.parentNode, fn));
    };

    // triggers when user clicks on thumbnail
    var onThumbnailsClick = function (e) {
        e = e || window.event;
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        var eTarget = e.target || e.srcElement;

        // find root element of slide
        var clickedListItem = closest(eTarget, function (el) {
            return (el.tagName && el.tagName.toUpperCase() === 'DIV');
        });

        if (!clickedListItem) {
            return;
        }

        // find index of clicked item by looping through all child nodes
        // alternatively, you may define index via data- attribute
        var clickedGallery = clickedListItem.parentNode,
                childNodes = clickedListItem.parentNode.childNodes,
                numChildNodes = childNodes.length,
                nodeIndex = 0,

                index;

        for (var i = 0; i < numChildNodes; i++) {
            if (childNodes[i].nodeType !== 1) {
                continue;
            }

            if (childNodes[i] === clickedListItem) {
                index = nodeIndex;
                break;
            }
            nodeIndex++;
        }

        if (index >= 0) {
            // open PhotoSwipe if valid index found
            openPhotoSwipe(index, clickedGallery);
        }
        return false;
    };

    // parse picture index and gallery index from URL (#&pid=1&gid=2)
    var photoswipeParseHash = function () {
        var hash = window.location.hash.substring(1),
                params = {};

        if (hash.length < 5) {
            return params;
        }

        var vars = hash.split('&');
        for (var i = 0; i < vars.length; i++) {
            if (!vars[i]) {
                continue;
            }
            var pair = vars[i].split('=');
            if (pair.length < 2) {
                continue;
            }
            params[pair[0]] = pair[1];
        }

        if (params.gid) {
            params.gid = parseInt(params.gid, 10);
        }

        return params;
    };

    var openPhotoSwipe = function (index, galleryElement, disableAnimation, fromURL) {
        var pswpElement = document.querySelectorAll('.pswp')[0],
                gallery,
                options,
                items;

        items = parseThumbnailElements(galleryElement);

        // define options (if needed)
        options = {
            showHideOpacity: true, getThumbBoundsFn: false,
            // define gallery index (for URL)
            galleryUID: galleryElement.getAttribute('data-pswp-uid'),


        };

        // PhotoSwipe opened from URL
        if (fromURL) {
            if (options.galleryPIDs) {
                // parse real index when custom PIDs are used
                // http://photoswipe.com/documentation/faq.html#custom-pid-in-url
                for (var j = 0; j < items.length; j++) {
                    if (items[j].pid == index) {
                        options.index = j;
                        break;
                    }
                }
            } else {
                // in URL indexes start from 1
                options.index = parseInt(index, 10) - 1;
            }
        } else {
            options.index = parseInt(index, 10);
        }

        // exit if index not found
        if (isNaN(options.index)) {
            return;
        }

        if (disableAnimation) {
            options.showAnimationDuration = 0;
        }

        // Pass data to PhotoSwipe and initialize it
        gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
        gallery.init();
    };

    // loop through all gallery elements and bind events
    var galleryElements = document.querySelectorAll(gallerySelector);

    for (var i = 0, l = galleryElements.length; i < l; i++) {
        galleryElements[i].setAttribute('data-pswp-uid', i + 1);
        galleryElements[i].onclick = onThumbnailsClick;
    }

    // Parse URL and open gallery if it contains #&pid=3&gid=1
    var hashData = photoswipeParseHash();
    if (hashData.pid && hashData.gid) {
        openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
    }
};

// country select dynamic state
(function() {

    var SelectCountry2 = function($select) {
        $select.data(this);

        var $form = $select.closest('form'),
            $container = $select.closest('.address-fields');

        $container = $container.length ? $container : $form;
        var $state = $container.find('.js-state'),
            $stateTmp = $container.find('.js-state-tmp');

        var countrySelect = {
            init: function() {
                var self = this,
                    $body = $('body');

                if (!$select.val()) {
                    $select.val(flexconfig.country_code);
                }

                if (flexconfig.topCountries.length) {

                    for (var i = flexconfig.topCountries.length - 1; i>=0;i--) {
                        var countryCode = flexconfig.topCountries[i];
                        if (!countryCode) continue;
                        var $option = $select.find('option[value="'+countryCode+'"]');

                        if ($option.length) {
                            $option.data('top', 1);
                            $select.prepend($option);

                            // last key
                            if (i === (flexconfig.topCountries.length - 1)) {
                                $option.data('last-top', 1);
                            }
                        }
                    }
                }

                $select.on('change.country', function () {
                    // delayed trigger change so other wrapper
                    // like select2 will work
                    $this = $(this);
                    (function($this) {
                        setTimeout(function() {
                            // make sure that we don't trigger change on first load
                            stateSelect.changeState($this.val());
                        }, 300);
                    })($this);
                }).trigger('change.country');

                this.reloadSelect();
            },

            getVal: function() {
                return $select.val();
            },

            preloadValue: function ($select, value, text) {
                var option = new Option(text, value, true, true);
                $select.append(option);
            },

            reloadSelect: function() {
                var self = this;

                // select 2 ajax function
                $select.select2(self._select2Option('- '+i18n.gettext('Select Country')+' -'));
            },

            _select2Option: function(placeHolder) {
                var self = this;

                return {
                    placeholder: placeHolder,
                    dropdownAutoWidth : true,
                    templateResult: function (item, container) {
                        if (!item.id) {
                            return item.text;
                        }

                        var isTop = $(item.element).data('top');
                        var lastTop = $(item.element).data('last-top');

                        var countryUrl = flex.getUrlCdn('/themes/frontend/default/images/circle-flags/');
                        var url = countryUrl;
                        var img = $("<img>", {
                            class: "img-flag",
                            width: 20,
                            src: url + item.element.value.toLowerCase() + ".svg"
                        });
                        var span = $("<div>", {
                            text: " " + item.text
                        });

                        if (isTop) {
                            $(container).addClass('select2-top-result');
                            if (lastTop) {
                                $(container).addClass('select2-top-last-result');
                            }
                        }
                        span.prepend(img);
                        return span;
                    }, // omitted for brevity, see the source of this page
                    dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
                }
            },
        };

        var stateSelect = {
            init: function() {
                $container.on('change.state', '.js-state-tmp', function () {
                    var $this = $(this);
                    $state
                        .val($this.val())
                        // make sure that we trigger change
                        // when we change the value of js state
                        .trigger('change');
                });

                $state.on('change.state', function () {
                    var $this = $(this);
                    $stateTmp
                        .val($this.val());
                    // make sure that we don't trigger change
                    // to avoid change loop
                });
            },
            changeState: function(countryCode) {
                // Note! Please be careful on adjusting this code
                // as order is dependent of the logic of this
                // to properly load data in same as billing and venue
                var self = this,
                    stateVal = $state.val();

                if (!$stateTmp.length) {
                    var select = self._createTmpState($state.attr('required'));
                    $(select).insertAfter($state);

                    // load again
                    $stateTmp = $container.find('.js-state-tmp');
                }

                $state.hide();
                $stateTmp.hide();

                if (typeof flexconfig.states[countryCode] !== 'undefined') {
                    var states = flexconfig.states[countryCode];

                    // clear previous options
                    $stateTmp.find('option').remove();

                    var options = '<option value="">' + i18n.gettext('Select State') + '</option>',
                        selected = '',
                        stateValFound = false;
                    for (var code in states) {
                        if (stateVal == code) {
                            selected = 'selected';
                            stateValFound = true;
                        } else {
                            selected = '';
                        }

                        options += '<option value="'+code+'" '+selected+'>'+states[code]+'</option>';
                    }

                    $stateTmp.html(options);

                    // refresh specific attributes
                    if ($state.is(':disabled')) {
                        $stateTmp.attr('disabled', $state.is(':disabled'));
                    } else {
                        $stateTmp.removeAttr('disabled');
                    }

                    if ($state.is('[readonly]')) {
                        $stateTmp.attr('readonly', 'readonly');
                    } else {
                        $stateTmp.removeAttr('readonly');
                    }

                    // if state val not found, then empty the main state
                    if (!stateValFound) {
                        $state.val('');
                    }

                    $stateTmp.show();
                } else {
                    $state.show();
                }
            },

            _createTmpState: function(isRequire) {
                isRequire = typeof isRequire !== 'undefined' ? isRequire : false;
                var addClass = $state.hasClass('form-control-lg') ? ' form-control-lg ' : ' ';
                addClass += $state.hasClass('form-control-md') ? ' form-control-md ' : ' ';
                return '<select name="state_tmp" class="js-state-tmp form-control '+addClass+'" '+(isRequire ? 'required' : '')+'></select>';
            }
        }

        countrySelect.init();
        stateSelect.init();
    };


    flex.globalPlugin.register(function($parent) {
        $parent.find('.js-country').each(function() {
            new SelectCountry2($(this));
        });
    });
})(jQuery);

$(function() {
    flex.venue = typeof flex.venue !== 'undefined' ? flex.venue : {};

    flex.venue.selectVenue = function(options) {
        var self = this;
        this.options = jQuery.extend({
            $select: $('.js-select-venue'),
            $selectRoom: $('.js-select-main-room'),
            dropdownParent: false,
            formData: {},
            onProccessResultsData: function(data) {
                return data;
            },
            onPostData: function(data) {
                return data;
            },
            onChangeRoom: function(data) {},
            change: function() {},
            // if loading ajax or not
            loading: function(loading) {},
        }, options);

        this.$select = this.options.$select;
        this.$selectRoom = this.options.$selectRoom;
        // identifier on which select it triggers the toggle new customer popup
        // in this way, we can make sure that it is getting the right data for the current select shown.
        this.uniqueId = flex.utils.uniqueId();

        this._perPage = 20;
        this.reloadSelect(this.options.$select);
        this.reloadSelectRoom(this.options.$selectRoom);

        // set me instance
        this.$select.data('selectVenueInstance', this);
        this.lastKeySelect2UpValue = '';


        // temporary fixes to get the venue name name
        $('body').on('keyup', '.select2-container--open .select2-search__field', function() {
            self.lastKeySelect2UpValue = $(this).val();
        });
    };

    flex.venue.selectVenue.prototype = {

        getVal: function() {
            return this.$select.val();
        },

        preloadValue: function ($select, value, text) {
            var option = new Option(text, value, true, true);
            $select.append(option);
        },


        reloadSelect: function($select) {
            var self = this;

            $select.change(function(e){
                self.options.change(e);

                self.updateRooms();
                self.showUrl($(this));
            });

            // select 2 ajax function
            $select.select2(self._select2Option(i18n.gettext('Select Venue')));

            self.venuePopup = new flex.venue.createPopup({
                $selectVenue: $select,
                success: function(results) {
                    var venue = results.venue;
                    $select.append('<option value="' + venue.id + '">' + flex.utils.e(venue.title) + '</option>');
                    $select.val(venue.id).trigger('change');
                }
            });

            $('body').on('click', '.js-toggle-new-venue-popup-'+self.uniqueId, function() {
                self.showCreateVenue();
            });

            self.showUrl($select);
        },

        reloadSelectRoom: function($selectRoom) {
            $selectRoom.select2({
                placeholder: i18n.gettext('-- Select Room --'),
                allowHtml: true,
                allowClear: true,
                tags: false,
            });
        },

        updateRoomValue: function(val) {
            this.$selectRoom.data('default-value', val);
            this.$selectRoom.val(val);

            return this;
        },

        updateRooms: function(callback) {
            callback = typeof callback !== 'undefined' ? callback : function() {};

            var self = this,
                venueId = self.getVal();

            self.options.loading(true);

            $.ajax({
                type: 'GET',
                url: flex.getUrl("venue/get_venue_details"),
                data: {id: venueId},
                dataType: 'json',
                success: function (data) {
                    self.$selectRoom.find('option:not([value=""])').remove();
                    self.options.loading(false);

                    $.each(data.rooms, function(key, room){
                        var option;
                        option = new Option(room.title, room.id);
                        self.$selectRoom.append(option);
                    });

                    self.updateRoomValue(self.$selectRoom.data('default-value'));

                    self.options.onChangeRoom(data);
                    callback();
                }
            });
        },

        showUrl: function ($select) {
            $select = typeof $select !== 'undefined' ? $select : this.$select;

            var $form = $select.closest('.js-select-venue-wrapper'),
                $arrowUrl = $form.find('.js-venue-arrow'),
                venueId = parseInt($select.val());

            $arrowUrl.hide();

            if ($arrowUrl.length && venueId) {
                $arrowUrl.attr("href", flex.getUrl('/venue/edit/'+venueId));
                $arrowUrl.show();
            }
        },

        showCreateVenue: function() {
            var self = this;
            self.venuePopup.show(jQuery.extend({
                title: self.lastKeySelect2UpValue
            }, self.options.formData));
        },

        _select2Option: function(placeHolder) {
            var self = this;

            return {
                placeholder: placeHolder,
                minimumInputLength: 0,
                allowClear: true,
                ajax: {
                    url: flex.getUrl("venue/find_venue"),
                    dataType: 'json',
                    delay: 250,
                    data: function (params) { // page is the one-based page number tracked by Select2
                        var $this = $(this);
                        return self.options.onPostData.call(this, {
                            q: params.term, //search term
                            page: params.page, // page number
                            per_page: self._perPage,
                            venue_id: $this.val(),
                        });
                    },
                    processResults: function (data, params) {
                        var i, disabled, item;

                        // set disabled status if order id is set
                        self.options.onProccessResultsData.call(this, data);

                        return {
                            results: data.items,
                            pagination: {
                                more: ((params.page ? params.page : 1) * self._perPage) < data.total_count
                            }
                        };
                    }
                },
                templateResult: self._formatResult, // omitted for brevity, see the source of this page
                templateSelection: function (item) {
                    return flex.utils.e(item.title ? item.title : item.text);
                },
                dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
                escapeMarkup: function (m) {
                    return m;
                }, // we do not want to escape markup since we are displaying html in results
                language: {
                    errorLoading: function () {
                        return i18n.gettext("Searching...");
                    }
                }
            }
        },

        _formatResult: function (result) {
            if (result.loading) {
                return flex.utils.e(result.text);
            }

            var markup = '<div class="row-fluid">' +
                '<div class="span12">' +
                '<div class="row-fluid">';
            markup += '<div class="span12">' + flex.utils.e(result.title);

            markup += '</div></div></div></div>';

            return markup;
        },
    };

    flex.venue.createPopup = function(options) {
        var self = this;
        self.options = jQuery.extend({
            $selectVenue: $('.js-select-venue'),
            success: function() {},
        }, options);

        self.$popup = $('.js-new-venue-popup');
        self.$form = self.$popup.find('.js-form-update-venue');
    };

    flex.venue.createPopup.prototype = {
        hide: function() {
            this.$popup.modal('hide');
        },
        show: function(formData) {
            var self = this;

            formData = jQuery.extend({
                title: '',
                // and so on
            }, formData);

            self.$popup.modal('show');
            self.$form.unbind('submit').trigger('reset');

            // form data update
            $.each(formData, function(k, v) {
                var n = '[name=' + k + ']',
                    $field = self.$form.find(n);

                if ($field.length) {
                    if ($field.is(':radio') || $field.is(':checkbox')) {
                        $field.prop('checked', (parseInt(v)) ? true : false);
                    } else {
                        $field.val(v);
                    }
                }
            });

            self.$form.submit(function (e) {
                e.preventDefault();

                if (!self.$form.parsley().validate()){
                    return ;
                }

                $.ajax({
                    type: 'POST',
                    url: self.$form.attr('action'),
                    data: self.$form.serialize(),
                    dataType: 'json',
                    success: function (results) {

                        if (!results.success) {
                            var errMsgDisplay = self.$popup.find('.err-message');

                            errMsgDisplay.html(results.message);
                            errMsgDisplay.hide().fadeIn();

                            self.$popup
                                .scrollTop(0)
                                .find('.modal-body')
                                .scrollTop(0);

                            return false;
                        }

                        if (results.success) {
                            self.hide();
                            self.options.success(results);
                        }
                        return false;
                    }
                })
            });
        }
    };
});

(function() {
    flex.product = typeof flex.product !== 'undefined' ? flex.product : {};

    flex.product.attributeSelectors = function(options) {
        this.options = jQuery.extend({
            $wrapper: $('.js-select-attribute-wrapper'),
            loading: function(loading) {},
            change: function(data) {}, // when combination is changed
            targetUri: '/product/prepare_product_cart/',
        }, options);

        this.listeners();
    };

    flex.product.attributeSelectors.prototype = {

        listeners: function() {
            var self = this;
            self.options.$wrapper.find('.js-select-attribute').off('change.attribute').on('change.attribute', function () {
                var $this = $(this),
                    $wrapper = self.options.$wrapper,
                    productId = $wrapper.data('product-id'),
                    $inputs = $wrapper.find('input, select'),
                    $selectAttribute = $wrapper.find('.js-select-attribute'),
                    data = $inputs.serialize();

                self.loading(true);
                $selectAttribute.attr('disabled', true);
                self.getAttributes(productId, data, function(data) {
                    self.renderAttributes($wrapper, data.attribute_selections);
                    self.options.change(data);
                    data.attribute_selections.forEach(function (attribute_selection) {
                        if (attribute_selection.display_type == flexconfig.product_attributes.DISPLAY_TYPE_DROPDOWN) {
                            $wrapper.find('.js-select-attribute-' + attribute_selection.attribute_id).attr('disabled', false);
                        }
                    });

                    self.loading(false);
                });
            });
        },

        refresh: function() {
            this.options.$wrapper.find('.js-select-attribute').first().trigger('change');
        },

        getAttributes: function(productId, data, $callback) {
            flex.post(flex.getUrl(this.options.targetUri+productId), data, function(data) {
                $callback(data);
            });
        },

        /**
         * packs and combo might contain products that has attributes
         *
         * @param $wrapper
         * @param selections
         */
        renderAttributes: function ($wrapper, selections) {
            var current, $select, options, selectedOption, parentElement;
            for (var x in selections) {
                current = selections[x];
                options = current.options;
                selectedOption = current.selected_option_id,
                $element = $wrapper.find('.js-select-attribute-'+current.attribute_id);
                parentElement = $element.closest('.form-select-control');

                if (!current.display_type || parentElement.parents('.combo-option-attributes').length) {
                    current.display_type = flexconfig.product_attributes.DISPLAY_TYPE_DROPDOWN;
                }

                if (current.display_type == flexconfig.product_attributes.DISPLAY_TYPE_DROPDOWN) {
                    $element.empty();

                    var option = new Option(current.placeholder, '', false, false);
                    option.disabled = true;
                    $element.append(option);

                    $.each(options, function(k, v) {
                        var isSelected = (v.value == selectedOption),
                            option = new Option(v.label, v.value, false, isSelected);

                        $element.append(option);
                    });
                } else {
                    $element.attr('disabled', true);
                    parentElement.empty();
                    $.each(options, function(k, v) {
                        let isSelected = (v.value == selectedOption),
                            inputName = $('input[name=attributes_post_key]').val(),
                            radioElementContainer = $('<label>').addClass('custom-control custom-radio');

                        let radioElement = $('<input>')
                            .addClass(`custom-control-input js-select-attribute js-select-attribute-${current.attribute_id}`)
                            .attr({
                                type: 'radio',
                                value: v.value,
                                name: `${inputName}[${current.attribute_id}]`,
                                checked: isSelected
                            });

                        radioElementContainer.append(radioElement);
                        parentElement.append(
                            $('<label>').addClass('custom-control custom-radio')
                                .append(radioElement)
                                .append($('<span>').addClass('custom-control-indicator').html(''))
                                .append($('<span>').addClass('custom-control-description').html(v.label))
                        );
                    });

                    this.listeners();
                }
            }
        },

        loading: function(loading) {
            loading = typeof loading !== 'undefined' ? loading : true;
            var self = this,
                $wrapper = self.options.$wrapper;

            $wrapper.removeClass('loading');
            if (loading) {
                $wrapper.addClass('loading');
            }

            // run options callback
            self.options.loading(loading);
        },
    };
})();

flex.addressBookFullAddressFormat = function(addressBook) {
    let fullAddress = [];

    if (addressBook) {
        fullAddress.push(addressBook.address);
        fullAddress.push(addressBook.address2);
        fullAddress.push(addressBook.suburb);
        fullAddress.push(addressBook.state);
        fullAddress.push(addressBook.postcode);

        return fullAddress.map(e => e ? String(e).trim() : '').filter(e => e).join(', ');
    }

    return '';
};

flex.sendErrorMessage = function (message, stack) {
    // do not send
    if (message && stack) {
        flex.post('order/js_error_handler', {
            message: message,
            stack: stack,
            browser: navigator.userAgent,
            url: window.location.href,
        });
    }
};

// error handler
(function() {
    $(window).error(function(e) {
        if (typeof e.originalEvent === 'undefined') {
            return ;
        }
        var message = e.originalEvent.error.message,
            stack = e.originalEvent.error.stack;
        console.log(message);
        console.log(stack);

        flex.sendErrorMessage(message, stack);
    });

    window.ajaxErrorReportSent = false;
    $( document ).ajaxError(function(e, response, request) {
        // do not send for now
        return ;

        // only send once the ajax error
        if (window.ajaxErrorReportSent) {
            return ;
        }


        if (!response.status) {
            return ;
        }

        if (response.status && !(response.status >= 400 && response.status <= 600)){
            return ;
        }

        var message = 'Ajax Error ('+request.url+':'+request.type+'): ' + response.status + '-' + response.responseText,
            stack;

        flex.sendErrorMessage(message, stack);

        window.ajaxErrorReportSent = true;
    });
})(jQuery);


flex.websocket = {
    connectAttempt: 0,
    maxAttempt: 8,
    connectAttemptTimeout: false,
    startTriggered: false,
    online: true,
    conn: false,
    subscriptions: [],

    subscribe: function(topic, callback) {
        //return ;

        this.subscriptions.push({
            topic: topic,
            callback: callback,
        });

        if (this.conn) {
            // if connection was published
            this.subscribeToConnection(this.conn, topic, callback);
        } else if (this.startTriggered) {
            // start might be triggered but had no subscription
            // so connection was not set
            this.start();
        }
    },

    topicPrefix: function(topic) {
        //return ;
        return flexconfig.socketTicketPrefix+topic;
    },

    subscribeToConnection: function(conn, topic, callback) {
        //return ;
        topic = this.topicPrefix(topic);
        var self = this;
        conn.subscribe(topic, function (topic, data) {
            if (typeof data === 'string' && data === 'failed') {
                console.warn(topic, 'websocket: subscription failed to `'+topic+'`');
                console.log('Subscription failed. Please refresh the page to try again.');
                return ;
            }

            // before calling the callback, we need to check if a message is for a heartbeat
            // server sends a ping, and we forward a pong. this is to make sure that every connection was closed properly on
            // the server side
            console.log('received...');
            console.log(data);
            if (Object.keys(data).length === 1 && data.ping) {
                self.heartbeat(conn, topic);
                return ;
            }

            callback(topic, data);
        });
    },

    heartbeat: function(conn, topic) {
        //return ;
        conn.publish(topic, {
            pong: 1
        });
    },

    checkOnlineStatus: function() {
        //return ;
        if (navigator.onLine) {
            if (!this.conn && !this.online) {
                this.start();
            }
            this.online = true;
        } else {
            this.online = false;
        }
    },

    start: function() {
        //return ;
        var self = this;
        self.startTriggered = true;

        if (!self.startTriggered) {
            setTimeout(function() {
                self.checkOnlineStatus();
            }, 2000);
        }

        if (self.subscriptions && self.subscriptions.length) {
            var conn = new ab.Session(flexconfig.socketEndpoint + '?token=' + flexconfig.socketToken,
                function() {
                    console.info('WebSocket connection created.');

                    var subscription;
                    for (var x in self.subscriptions) {
                        subscription = self.subscriptions[x];
                        self.subscribeToConnection(conn, subscription.topic, subscription.callback);
                    }
                    self.conn = conn;

                    // reset connect attempt
                    clearTimeout(self.connectAttemptTimeout);
                    self.connectAttempt = 0;
                },
                function(code, reason) {
                    console.log(code, reason);
                    // if internet connection was lost, and user connected again
                    // websocket auto try to connect and check if existing connection is okay
                    // which chances are it was disconnected
                    console.warn('WebSocket connection closed');
                    console.warn(i18n.gettext('Unable to connect to websocket. Please reload your browser'))

                    // if offline and we attempted to connect twice.
                    // then never connect
                    if (self.connectAttempt >= self.maxAttempt) {
                        return ;
                    }

                    self.connectAttemptTimeout = setTimeout(function() {
                        self.connectAttempt ++;
                        console.log('Attempting to connect to websocket..');
                        self.start();
                    }, self.connectAttempt <= 2 ? 2000 : 8000); // for the first and second attempt, attempt should be just 2 seconds
                },
                {'skipSubprotocolCheck': true}
            );
            window.conn = conn;
        }
    }
};
