;
define("js/bundles/bootstrap", function(){});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

;define('Vaimo_BauhausSe/js/mock/momentjs-mock', [], function () {
    'use strict';

    var Moment = function (value, format) {
        try {
            this.date = new Date(value);
        } catch (e) {}
    };

    Moment.prototype.isValid = function () {
        try {
            return !!this.date && this.date.toString() !== new Date(0).toString();
        } catch (e) {
            return false;
        }
    };

    Moment.prototype.format = function () {
        var result = '';

        try {
            result = this.date ? this.date.toString() : '';
        } catch (e) {}

        return result;
    };

    Moment.prototype.unix = function () {
        try {
            return this.date.getTime();
        } catch (e) {
            return -1;
        }
    };

    var momentFactory = function (value, format) {
        return new Moment(value, format);
    };

    momentFactory.utc = function (value, format) {
        return new Moment(value, format);
    };

    return momentFactory;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Customer/js/section-config',['underscore'], function (_) {
    'use strict';

    var baseUrls = [],
        sections = [],
        clientSideSections = [],
        sectionNames = [],
        canonize;

    /**
     * @param {String} url
     * @return {String}
     */
    canonize = function (url) {
        var route = url;

        _.some(baseUrls, function (baseUrl) {
            route = url.replace(baseUrl, '');

            return route !== url;
        });

        return route.replace(/^\/?index.php\/?/, '').toLowerCase();
    };

    return {
        /**
         * Returns a list of sections which should be invalidated for given URL.
         * @param {String} url - URL which was requested.
         * @return {Object} - List of sections to invalidate.
         */
        getAffectedSections: function (url) {
            var route = canonize(url),
                actions = _.find(sections, function (val, section) {
                    var matched;

                    // Covers the case where "*" works as a glob pattern.
                    if (section.indexOf('*') >= 0) {
                        section = section.replace(/\*/g, '[^/]+') + '$';
                        matched = route.match(section);

                        return matched && matched[0] === route;
                    }

                    return route.indexOf(section) === 0;
                });

            return _.union(_.toArray(actions), sections['*']);
        },

        /**
         * Filters the list of given sections to the ones defined as client side.
         * @param {Object} allSections - List of sections to check.
         * @return {Object} - List of filtered sections.
         */
        filterClientSideSections: function (allSections) {
            return _.difference(allSections, clientSideSections);
        },

        /**
         * Tells if section is defined as client side.
         * @param {String} sectionName - Name of the section to check.
         * @return {Boolean}
         */
        isClientSideSection: function (sectionName) {
            return _.contains(clientSideSections, sectionName);
        },

        /**
         * Returns array of section names.
         * @returns {Array}
         */
        getSectionNames: function () {
            return sectionNames;
        },

        /**
         * @param {Object} options
         * @constructor
         */
        'Magento_Customer/js/section-config': function (options) {
            baseUrls = options.baseUrls;
            sections = options.sections;
            clientSideSections = options.clientSideSections;
            sectionNames = options.sectionNames;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/* eslint-disable strict */
define('mage/url',[], function () {
    var baseUrl = '';

    return {
        /**
         * @param {String} url
         */
        setBaseUrl: function (url) {
            baseUrl = url;
        },

        /**
         * @param {String} path
         * @return {*}
         */
        build: function (path) {
            if (path.indexOf(baseUrl) !== -1) {
                return path;
            }

            return baseUrl + path;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/storage',['jquery', 'mage/url'], function ($, urlBuilder) {
    'use strict';

    return {
        /**
         * Perform asynchronous GET request to server.
         * @param {String} url
         * @param {Boolean} global
         * @param {String} contentType
         * @param {Object} headers
         * @returns {Deferred}
         */
        get: function (url, global, contentType, headers) {
            headers = headers || {};
            global = global === undefined ? true : global;
            contentType = contentType || 'application/json';

            return $.ajax({
                url: urlBuilder.build(url),
                type: 'GET',
                global: global,
                contentType: contentType,
                headers: headers
            });
        },

        /**
         * Perform asynchronous POST request to server.
         * @param {String} url
         * @param {String} data
         * @param {Boolean} global
         * @param {String} contentType
         * @param {Object} headers
         * @param {Boolean} async
         * @returns {Deferred}
         */
        post: function (url, data, global, contentType, headers, async) {
            headers = headers || {};
            global = global === undefined ? true : global;
            contentType = contentType || 'application/json';
            async = async === undefined ? true : async;

            return $.ajax({
                url: urlBuilder.build(url),
                type: 'POST',
                data: data,
                global: global,
                contentType: contentType,
                headers: headers,
                async: async
            });
        },

        /**
         * Perform asynchronous PUT request to server.
         * @param {String} url
         * @param {String} data
         * @param {Boolean} global
         * @param {String} contentType
         * @param {Object} headers
         * @returns {Deferred}
         */
        put: function (url, data, global, contentType, headers) {
            var ajaxSettings = {};

            headers = headers || {};
            global = global === undefined ? true : global;
            contentType = contentType || 'application/json';
            ajaxSettings.url = urlBuilder.build(url);
            ajaxSettings.type = 'PUT';
            ajaxSettings.data = data;
            ajaxSettings.global = global;
            ajaxSettings.contentType = contentType;
            ajaxSettings.headers = headers;

            return $.ajax(ajaxSettings);
        },

        /**
         * Perform asynchronous DELETE request to server.
         * @param {String} url
         * @param {Boolean} global
         * @param {String} contentType
         * @param {Object} headers
         * @returns {Deferred}
         */
        delete: function (url, global, contentType, headers) {
            headers = headers || {};
            global = global === undefined ? true : global;
            contentType = contentType || 'application/json';

            return $.ajax({
                url: urlBuilder.build(url),
                type: 'DELETE',
                global: global,
                contentType: contentType,
                headers: headers
            });
        }
    };
});

/*! js-cookie v3.0.5 | MIT */
;
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define('js-cookie/js.cookie',factory) :
            (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (function () {
                var current = global.Cookies;
                var exports = global.Cookies = factory();
                exports.noConflict = function () { global.Cookies = current; return exports; };
            })());
})(this, (function () { 'use strict';

    /* eslint-disable no-var */
    function assign (target) {
        for (var i = 1; i < arguments.length; i++) {
            var source = arguments[i];
            for (var key in source) {
                target[key] = source[key];
            }
        }
        return target
    }
    /* eslint-enable no-var */

    /* eslint-disable no-var */
    var defaultConverter = {
        read: function (value) {
            if (value[0] === '"') {
                value = value.slice(1, -1);
            }
            return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent)
        },
        write: function (value) {
            return encodeURIComponent(value).replace(
                /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,
                decodeURIComponent
            )
        }
    };
    /* eslint-enable no-var */

    /* eslint-disable no-var */

    function init (converter, defaultAttributes) {
        function set (name, value, attributes) {
            if (typeof document === 'undefined') {
                return
            }

            attributes = assign({}, defaultAttributes, attributes);

            if (typeof attributes.expires === 'number') {
                attributes.expires = new Date(Date.now() + attributes.expires * 864e5);
            }
            if (attributes.expires) {
                attributes.expires = attributes.expires.toUTCString();
            }

            name = encodeURIComponent(name)
                .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
                .replace(/[()]/g, escape);

            var stringifiedAttributes = '';
            for (var attributeName in attributes) {
                if (!attributes[attributeName]) {
                    continue
                }

                stringifiedAttributes += '; ' + attributeName;

                if (attributes[attributeName] === true) {
                    continue
                }

                // Considers RFC 6265 section 5.2:
                // ...
                // 3.  If the remaining unparsed-attributes contains a %x3B (";")
                //     character:
                // Consume the characters of the unparsed-attributes up to,
                // not including, the first %x3B (";") character.
                // ...
                stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
            }

            return (document.cookie =
                name + '=' + converter.write(value, name) + stringifiedAttributes)
        }

        function get (name) {
            if (typeof document === 'undefined' || (arguments.length && !name)) {
                return
            }

            // To prevent the for loop in the first place assign an empty array
            // in case there are no cookies at all.
            var cookies = document.cookie ? document.cookie.split('; ') : [];
            var jar = {};
            for (var i = 0; i < cookies.length; i++) {
                var parts = cookies[i].split('=');
                var value = parts.slice(1).join('=');

                try {
                    var found = decodeURIComponent(parts[0]);
                    jar[found] = converter.read(value, found);

                    if (name === found) {
                        break
                    }
                } catch (e) {}
            }

            return name ? jar[name] : jar
        }

        return Object.create(
            {
                set,
                get,
                remove: function (name, attributes) {
                    set(
                        name,
                        '',
                        assign({}, attributes, {
                            expires: -1
                        })
                    );
                },
                withAttributes: function (attributes) {
                    return init(this.converter, assign({}, this.attributes, attributes))
                },
                withConverter: function (converter) {
                    return init(assign({}, this.converter, converter), this.attributes)
                }
            },
            {
                attributes: { value: Object.freeze(defaultAttributes) },
                converter: { value: Object.freeze(converter) }
            }
        )
    }

    var api = init(defaultConverter, { path: '/' });
    /* eslint-enable no-var */

    return api;

}));

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('js-cookie/cookie-wrapper',[
    'jquery',
    'js-cookie/js.cookie'
], function ($, cookie) {
    'use strict';

    window.Cookies = window.Cookies || cookie;

    var config = $.cookie = function (key, value, options) {
        if (value !== undefined) {
            options = $.extend({}, config.defaults, options);

            return cookie.set(key, value, options);
        }

        var result = key ? undefined : {},
            cookies = document.cookie ? document.cookie.split('; ') : [],
            i;

        for (i = 0; i < cookies.length; i++) {
            var parts = cookies[i].split('='),
                name = config.raw ? parts.shift() : decodeURIComponent(parts.shift()),
                cookieValue = parts.join('=');

            if (key && key === name) {
                result = decodeURIComponent(cookieValue.replace('/\\+/g', ' '));
                break;
            }

            if (!key && (cookieValue = decodeURIComponent(cookieValue.replace('/\\+/g', ' '))) !== undefined) {
                result[name] = cookieValue;
            }
        }

        return result;
    };

    config.defaults = {};

    $.removeCookie = function (key, options) {
        if ($.cookie(key) === undefined) {
            return false;
        }

        $.cookie(key, '', $.extend({}, options, { expires: -1 }));
        return !$.cookie(key);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('jquery/jquery.cookie',[
    'jquery',
    'js-cookie/cookie-wrapper'
], function () {

});

/*
 * JS Storage Plugin
 *
 * Copyright (c) 2019 Julien Maurel
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Project home:
 * https://github.com/julien-maurel/js-storage
 *
 * Version: 1.1.0
 */
(function (factory) {
    var registeredInModuleLoader = false;
    if (typeof define === 'function' && define.amd) {
        define('js-storage/js.storage',['jquery', 'jquery/jquery.cookie'], factory);
        registeredInModuleLoader = true;
    }
    if (typeof exports === 'object') {
        module.exports = factory();
        registeredInModuleLoader = true;
    }
    if (!registeredInModuleLoader) {
        var OldStorages = window.Storages;
        var api = window.Storages = factory();
        api.noConflict = function () {
            window.Storages = OldStorages;
            return api;
        };
    }
}(function () {
    // Variables used by utilities functions (like isPlainObject...)
    var class2type = {};
    var toString = class2type.toString;
    var hasOwn = class2type.hasOwnProperty;
    var fnToString = hasOwn.toString;
    var ObjectFunctionString = fnToString.call(Object);
    var getProto = Object.getPrototypeOf;
    var apis = {};

    // Prefix to use with cookie fallback
    var cookie_local_prefix = "ls_";
    var cookie_session_prefix = "ss_";

    // Get items from a storage
    function _get() {
        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], vi, ret, tmp, i, j;
        if (l < 1) {
            throw new Error('Minimum 1 argument must be given');
        } else if (Array.isArray(a0)) {
            // If second argument is an array, return an object with value of storage for each item in this array
            ret = {};
            for (i in a0) {
                if (a0.hasOwnProperty(i)) {
                    vi = a0[i];
                    try {
                        ret[vi] = JSON.parse(s.getItem(vi));
                    } catch (e) {
                        ret[vi] = s.getItem(vi);
                    }
                }
            }
            return ret;
        } else if (l == 1) {
            // If only 1 argument, return value directly
            try {
                return JSON.parse(s.getItem(a0));
            } catch (e) {
                return s.getItem(a0);
            }
        } else {
            // If more than 1 argument, parse storage to retrieve final value to return it
            // Get first level
            try {
                ret = JSON.parse(s.getItem(a0));
                if (!ret) {
                    throw new ReferenceError(a0 + ' is not defined in this storage');
                }
            } catch (e) {
                throw new ReferenceError(a0 + ' is not defined in this storage');
            }
            // Parse next levels
            for (i = 1; i < l - 1; i++) {
                ret = ret[a[i]];
                if (ret === undefined) {
                    throw new ReferenceError([].slice.call(a, 0, i + 1).join('.') + ' is not defined in this storage');
                }
            }
            // If last argument is an array, return an object with value for each item in this array
            // Else return value normally
            if (Array.isArray(a[i])) {
                tmp = ret;
                ret = {};
                for (j in a[i]) {
                    if (a[i].hasOwnProperty(j)) {
                        ret[a[i][j]] = tmp[a[i][j]];
                    }
                }
                return ret;
            } else {
                return ret[a[i]];
            }
        }
    }

    // Set items of a storage
    function _set() {
        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], a1 = a[1], vi, to_store = isNaN(a1) ? {} : [], type, tmp, i;
        if (l < 1 || !_isPlainObject(a0) && l < 2) {
            throw new Error('Minimum 2 arguments must be given or first parameter must be an object');
        } else if (_isPlainObject(a0)) {
            // If first argument is an object, set values of storage for each property of this object
            for (i in a0) {
                if (a0.hasOwnProperty(i)) {
                    vi = a0[i];
                    if (!_isPlainObject(vi) && !this.alwaysUseJson) {
                        s.setItem(i, vi);
                    } else {
                        s.setItem(i, JSON.stringify(vi));
                    }
                }
            }
            return a0;
        } else if (l == 2) {
            // If only 2 arguments, set value of storage directly
            if (typeof a1 === 'object' || this.alwaysUseJson) {
                s.setItem(a0, JSON.stringify(a1));
            } else {
                s.setItem(a0, a1);
            }
            return a1;
        } else {
            // If more than 3 arguments, parse storage to retrieve final node and set value
            // Get first level
            try {
                tmp = s.getItem(a0);
                if (tmp != null) {
                    to_store = JSON.parse(tmp);
                }
            } catch (e) {
            }
            tmp = to_store;
            // Parse next levels and set value
            for (i = 1; i < l - 2; i++) {
                vi = a[i];
                type = isNaN(a[i + 1]) ? "object" : "array";
                if (!tmp[vi] || type == "object" && !_isPlainObject(tmp[vi]) || type == "array" && !Array.isArray(tmp[vi])) {
                    if (type == "array") tmp[vi] = [];
                    else tmp[vi] = {};
                }
                tmp = tmp[vi];
            }
            tmp[a[i]] = a[i + 1];
            s.setItem(a0, JSON.stringify(to_store));
            return to_store;
        }
    }

    // Remove items from a storage
    function _remove() {
        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], to_store, tmp, i, j;
        if (l < 1) {
            throw new Error('Minimum 1 argument must be given');
        } else if (Array.isArray(a0)) {
            // If first argument is an array, remove values from storage for each item of this array
            for (i in a0) {
                if (a0.hasOwnProperty(i)) {
                    s.removeItem(a0[i]);
                }
            }
            return true;
        } else if (l == 1) {
            // If only 2 arguments, remove value from storage directly
            s.removeItem(a0);
            return true;
        } else {
            // If more than 2 arguments, parse storage to retrieve final node and remove value
            // Get first level
            try {
                to_store = tmp = JSON.parse(s.getItem(a0));
            } catch (e) {
                throw new ReferenceError(a0 + ' is not defined in this storage');
            }
            // Parse next levels and remove value
            for (i = 1; i < l - 1; i++) {
                tmp = tmp[a[i]];
                if (tmp === undefined) {
                    throw new ReferenceError([].slice.call(a, 1, i).join('.') + ' is not defined in this storage');
                }
            }
            // If last argument is an array,remove value for each item in this array
            // Else remove value normally
            if (Array.isArray(a[i])) {
                for (j in a[i]) {
                    if (a[i].hasOwnProperty(j)) {
                        delete tmp[a[i][j]];
                    }
                }
            } else {
                delete tmp[a[i]];
            }
            s.setItem(a0, JSON.stringify(to_store));
            return true;
        }
    }

    // Remove all items from a storage
    function _removeAll(reinit_ns) {
        var keys = _keys.call(this), i;
        for (i in keys) {
            if (keys.hasOwnProperty(i)) {
                _remove.call(this, keys[i]);
            }
        }
        // Reinitialize all namespace storages
        if (reinit_ns) {
            for (i in apis.namespaceStorages) {
                if (apis.namespaceStorages.hasOwnProperty(i)) {
                    _createNamespace(i);
                }
            }
        }
    }

    // Check if items of a storage are empty
    function _isEmpty() {
        var l = arguments.length, a = arguments, a0 = a[0], i;
        if (l == 0) {
            // If no argument, test if storage is empty
            return (_keys.call(this).length == 0);
        } else if (Array.isArray(a0)) {
            // If first argument is an array, test each item of this array and return true only if all items are empty
            for (i = 0; i < a0.length; i++) {
                if (!_isEmpty.call(this, a0[i])) {
                    return false;
                }
            }
            return true;
        } else {
            // If at least 1 argument, try to get value and test it
            try {
                var v = _get.apply(this, arguments);
                // Convert result to an object (if last argument is an array, _get return already an object) and test each item
                if (!Array.isArray(a[l - 1])) {
                    v = {'totest': v};
                }
                for (i in v) {
                    if (v.hasOwnProperty(i) && !(
                        (_isPlainObject(v[i]) && _isEmptyObject(v[i])) ||
                        (Array.isArray(v[i]) && !v[i].length) ||
                        (typeof v[i] !== 'boolean' && !v[i])
                    )) {
                        return false;
                    }
                }
                return true;
            } catch (e) {
                return true;
            }
        }
    }

    // Check if items of a storage exist
    function _isSet() {
        var l = arguments.length, a = arguments, a0 = a[0], i;
        if (l < 1) {
            throw new Error('Minimum 1 argument must be given');
        }
        if (Array.isArray(a0)) {
            // If first argument is an array, test each item of this array and return true only if all items exist
            for (i = 0; i < a0.length; i++) {
                if (!_isSet.call(this, a0[i])) {
                    return false;
                }
            }
            return true;
        } else {
            // For other case, try to get value and test it
            try {
                var v = _get.apply(this, arguments);
                // Convert result to an object (if last argument is an array, _get return already an object) and test each item
                if (!Array.isArray(a[l - 1])) {
                    v = {'totest': v};
                }
                for (i in v) {
                    if (v.hasOwnProperty(i) && !(v[i] !== undefined && v[i] !== null)) {
                        return false;
                    }
                }
                return true;
            } catch (e) {
                return false;
            }
        }
    }

    // Get keys of a storage or of an item of the storage
    function _keys() {
        var storage = this._type, l = arguments.length, s = window[storage], keys = [], o = {};
        // If at least 1 argument, get value from storage to retrieve keys
        // Else, use storage to retrieve keys
        if (l > 0) {
            o = _get.apply(this, arguments);
        } else {
            o = s;
        }
        if (o && o._cookie) {
            // If storage is a cookie, use js-cookie to retrieve keys
            var cookies = Cookies.get();
            for (var key in cookies) {
                if (cookies.hasOwnProperty(key) && key != '') {
                    keys.push(key.replace(o._prefix, ''));
                }
            }
        } else {
            for (var i in o) {
                if (o.hasOwnProperty(i)) {
                    keys.push(i);
                }
            }
        }
        return keys;
    }

    // Create new namespace storage
    function _createNamespace(name) {
        if (!name || typeof name != "string") {
            throw new Error('First parameter must be a string');
        }
        if (storage_available) {
            if (!window.localStorage.getItem(name)) {
                window.localStorage.setItem(name, '{}');
            }
            if (!window.sessionStorage.getItem(name)) {
                window.sessionStorage.setItem(name, '{}');
            }
        } else {
            if (!window.localCookieStorage.getItem(name)) {
                window.localCookieStorage.setItem(name, '{}');
            }
            if (!window.sessionCookieStorage.getItem(name)) {
                window.sessionCookieStorage.setItem(name, '{}');
            }
        }
        var ns = {
            localStorage: _extend({}, apis.localStorage, {_ns: name}),
            sessionStorage: _extend({}, apis.sessionStorage, {_ns: name})
        };
        if (cookies_available) {
            if (!window.cookieStorage.getItem(name)) {
                window.cookieStorage.setItem(name, '{}');
            }
            ns.cookieStorage = _extend({}, apis.cookieStorage, {_ns: name});
        }
        apis.namespaceStorages[name] = ns;
        return ns;
    }

    // Test if storage is natively available on browser
    function _testStorage(name) {
        var foo = 'jsapi';
        try {
            if (!window[name]) {
                return false;
            }
            window[name].setItem(foo, foo);
            window[name].removeItem(foo);
            return true;
        } catch (e) {
            return false;
        }
    }

    // Test if a variable is a plain object (from jQuery)
    function _isPlainObject(obj) {
        var proto, Ctor;

        // Detect obvious negatives
        // Use toString instead of jQuery.type to catch host objects
        if (!obj || toString.call(obj) !== "[object Object]") {
            return false;
        }

        proto = getProto(obj);

        // Objects with no prototype (e.g., `Object.create( null )`) are plain
        if (!proto) {
            return true;
        }

        // Objects with prototype are plain iff they were constructed by a global Object function
        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
        return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
    }

    // Test if a variable is an empty object (from jQuery)
    function _isEmptyObject(obj) {
        var name;

        for (name in obj) {
            return false;
        }
        return true;
    }

    // Merge objects
    function _extend() {
        var i = 1;
        var result = arguments[0];
        for (; i < arguments.length; i++) {
            var attributes = arguments[i];
            for (var key in attributes) {
                if (attributes.hasOwnProperty(key)) {
                    result[key] = attributes[key];
                }
            }
        }
        return result;
    }

    // Check if storages are natively available on browser and check is js-cookie is present
    var storage_available = _testStorage('localStorage');
    var cookies_available = typeof Cookies !== 'undefined';

    // Namespace object
    var storage = {
        _type: '',
        _ns: '',
        _callMethod: function (f, a) {
            a = Array.prototype.slice.call(a);
            var p = [], a0 = a[0];
            if (this._ns) {
                p.push(this._ns);
            }
            if (typeof a0 === 'string' && a0.indexOf('.') !== -1) {
                a.shift();
                [].unshift.apply(a, a0.split('.'));
            }
            [].push.apply(p, a);
            return f.apply(this, p);
        },
        // Define if plugin always use JSON to store values (even to store simple values like string, int...) or not
        alwaysUseJson: false,
        // Get items. If no parameters and storage have a namespace, return all namespace
        get: function () {
            if (!storage_available && !cookies_available){
                return null;
            }
            return this._callMethod(_get, arguments);
        },
        // Set items
        set: function () {
            var l = arguments.length, a = arguments, a0 = a[0];
            if (l < 1 || !_isPlainObject(a0) && l < 2) {
                throw new Error('Minimum 2 arguments must be given or first parameter must be an object');
            }
            if (!storage_available && !cookies_available){
                return null;
            }
            // If first argument is an object and storage is a namespace storage, set values individually
            if (_isPlainObject(a0) && this._ns) {
                for (var i in a0) {
                    if (a0.hasOwnProperty(i)) {
                        this._callMethod(_set, [i, a0[i]]);
                    }
                }
                return a0;
            } else {
                var r = this._callMethod(_set, a);
                if (this._ns) {
                    return r[a0.split('.')[0]];
                } else {
                    return r;
                }
            }
        },
        // Delete items
        remove: function () {
            if (arguments.length < 1) {
                throw new Error('Minimum 1 argument must be given');
            }
            if (!storage_available && !cookies_available){
                return null;
            }
            return this._callMethod(_remove, arguments);
        },
        // Delete all items
        removeAll: function (reinit_ns) {
            if (!storage_available && !cookies_available){
                return null;
            }
            if (this._ns) {
                this._callMethod(_set, [{}]);
                return true;
            } else {
                return this._callMethod(_removeAll, [reinit_ns]);
            }
        },
        // Items empty
        isEmpty: function () {
            if (!storage_available && !cookies_available){
                return null;
            }
            return this._callMethod(_isEmpty, arguments);
        },
        // Items exists
        isSet: function () {
            if (arguments.length < 1) {
                throw new Error('Minimum 1 argument must be given');
            }
            if (!storage_available && !cookies_available){
                return null;
            }
            return this._callMethod(_isSet, arguments);
        },
        // Get keys of items
        keys: function () {
            if (!storage_available && !cookies_available){
                return null;
            }
            return this._callMethod(_keys, arguments);
        }
    };

    // Use js-cookie for compatibility with old browsers and give access to cookieStorage
    if (cookies_available) {
        // sessionStorage is valid for one window/tab. To simulate that with cookie, we set a name for the window and use it for the name of the cookie
        if (!window.name) {
            window.name = Math.floor(Math.random() * 100000000);
        }
        var cookie_storage = {
            _cookie: true,
            _prefix: '',
            _expires: null,
            _path: null,
            _domain: null,
            _secure: false,
            setItem: function (n, v) {
                Cookies.set(this._prefix + n, v, {expires: this._expires, path: this._path, domain: this._domain, secure: this._secure});
            },
            getItem: function (n) {
                return Cookies.get(this._prefix + n);
            },
            removeItem: function (n) {
                return Cookies.remove(this._prefix + n, {path: this._path});
            },
            clear: function () {
                var cookies = Cookies.get();
                for (var key in cookies) {
                    if (cookies.hasOwnProperty(key) && key != '') {
                        if (!this._prefix && key.indexOf(cookie_local_prefix) === -1 && key.indexOf(cookie_session_prefix) === -1 || this._prefix && key.indexOf(this._prefix) === 0) {
                            Cookies.remove(key);
                        }
                    }
                }
            },
            setExpires: function (e) {
                this._expires = e;
                return this;
            },
            setPath: function (p) {
                this._path = p;
                return this;
            },
            setDomain: function (d) {
                this._domain = d;
                return this;
            },
            setSecure: function (s) {
                this._secure = s;
                return this;
            },
            setConf: function (c) {
                if (c.path) {
                    this._path = c.path;
                }
                if (c.domain) {
                    this._domain = c.domain;
                }
                if (c.secure) {
                    this._secure = c.secure;
                }
                if (c.expires) {
                    this._expires = c.expires;
                }
                return this;
            },
            setDefaultConf: function () {
                this._path = this._domain = this._expires = null;
                this._secure = false;
            }
        };
        if (!storage_available) {
            window.localCookieStorage = _extend({}, cookie_storage, {
                _prefix: cookie_local_prefix,
                _expires: 365 * 10,
                _secure: true
            });
            window.sessionCookieStorage = _extend({}, cookie_storage, {
                _prefix: cookie_session_prefix + window.name + '_',
                _secure: true
            });
        }
        window.cookieStorage = _extend({}, cookie_storage);
        // cookieStorage API
        apis.cookieStorage = _extend({}, storage, {
            _type: 'cookieStorage',
            setExpires: function (e) {
                window.cookieStorage.setExpires(e);
                return this;
            },
            setPath: function (p) {
                window.cookieStorage.setPath(p);
                return this;
            },
            setDomain: function (d) {
                window.cookieStorage.setDomain(d);
                return this;
            },
            setSecure: function (s) {
                window.cookieStorage.setSecure(s);
                return this;
            },
            setConf: function (c) {
                window.cookieStorage.setConf(c);
                return this;
            },
            setDefaultConf: function () {
                window.cookieStorage.setDefaultConf();
                return this;
            }
        });
    }

    // Get a new API on a namespace
    apis.initNamespaceStorage = function (ns) {
        return _createNamespace(ns);
    };
    if (storage_available) {
        // localStorage API
        apis.localStorage = _extend({}, storage, {_type: 'localStorage'});
        // sessionStorage API
        apis.sessionStorage = _extend({}, storage, {_type: 'sessionStorage'});
    } else {
        // localStorage API
        apis.localStorage = _extend({}, storage, {_type: 'localCookieStorage'});
        // sessionStorage API
        apis.sessionStorage = _extend({}, storage, {_type: 'sessionCookieStorage'});
    }
    // List of all namespace storage
    apis.namespaceStorages = {};
    // Remove all items in all storages
    apis.removeAllStorages = function (reinit_ns) {
        apis.localStorage.removeAll(reinit_ns);
        apis.sessionStorage.removeAll(reinit_ns);
        if (apis.cookieStorage) {
            apis.cookieStorage.removeAll(reinit_ns);
        }
        if (!reinit_ns) {
            apis.namespaceStorages = {};
        }
    };
    // About alwaysUseJson
    // By default, all values are string on html storages and the plugin don't use json to store simple values (strings, int, float...)
    // So by default, if you do storage.setItem('test',2), value in storage will be "2", not 2
    // If you set this property to true, all values set with the plugin will be stored as json to have typed values in any cases
    apis.alwaysUseJsonInStorage = function (value) {
        storage.alwaysUseJson = value;
        apis.localStorage.alwaysUseJson = value;
        apis.sessionStorage.alwaysUseJson = value;
        if (apis.cookieStorage) {
            apis.cookieStorage.alwaysUseJson = value;
        }
    };

    return apis;
}));

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('jquery/jquery-storageapi',[
    'jquery',
    'js-storage/js.storage'
], function ($, storage) {
    'use strict';

    if (window.cookieStorage) {
        var cookiesConfig = window.cookiesConfig || {};

        $.extend(window.cookieStorage, {
            _secure: !!cookiesConfig.secure,
            _samesite: cookiesConfig.samesite ? cookiesConfig.samesite : 'lax',

            /**
             * Set value under name
             * @param {String} name
             * @param {String} value
             * @param {Object} [options]
             */
            setItem: function (name, value, options) {
                var _default = {
                    expires: this._expires,
                    path: this._path,
                    domain: this._domain,
                    secure: this._secure,
                    samesite: this._samesite
                };

                $.cookie(this._prefix + name, value, $.extend(_default, options || {}));
            },

            /**
             * Set default options
             * @param {Object} c
             * @returns {storage}
             */
            setConf: function (c) {
                if (c.path) {
                    this._path = c.path;
                }

                if (c.domain) {
                    this._domain = c.domain;
                }

                if (c.expires) {
                    this._expires = c.expires;
                }

                if (typeof c.secure !== 'undefined') {
                    this._secure = c.secure;
                }

                if (typeof c.samesite !== 'undefined') {
                    this._samesite = c.samesite;
                }

                return this;
            }
        });
    }

    $.alwaysUseJsonInStorage = $.alwaysUseJsonInStorage || storage.alwaysUseJsonInStorage;
    $.cookieStorage = $.cookieStorage || storage.cookieStorage;
    $.initNamespaceStorage = $.initNamespaceStorage || storage.initNamespaceStorage;
    $.localStorage = $.localStorage || storage.localStorage;
    $.namespaceStorages = $.namespaceStorages || storage.namespaceStorages;
    $.removeAllStorages = $.removeAllStorages || storage.removeAllStorages;
    $.sessionStorage = $.sessionStorage || storage.sessionStorage;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Customer/js/customer-data',[
    'jquery',
    'underscore',
    'ko',
    'Magento_Customer/js/section-config',
    'mage/url',
    'mage/storage',
    'jquery/jquery-storageapi'
], function ($, _, ko, sectionConfig, url) {
    'use strict';

    var options = {},
        storage,
        storageInvalidation,
        invalidateCacheBySessionTimeOut,
        invalidateCacheByCloseCookieSession,
        dataProvider,
        buffer,
        customerData,
        deferred = $.Deferred();

    url.setBaseUrl(window.BASE_URL);
    options.sectionLoadUrl = url.build('customer/section/load');

    /**
     * @param {Object} invalidateOptions
     */
    invalidateCacheBySessionTimeOut = function (invalidateOptions) {
        var date;

        if (new Date($.localStorage.get('mage-cache-timeout')) < new Date()) {
            storage.removeAll();
        }
        date = new Date(Date.now() + parseInt(invalidateOptions.cookieLifeTime, 10) * 1000);
        $.localStorage.set('mage-cache-timeout', date);
    };

    /**
     * Invalidate Cache By Close Cookie Session
     */
    invalidateCacheByCloseCookieSession = function () {
        var isLoggedIn = parseInt(options.isLoggedIn, 10) || 0;

        if (!$.cookieStorage.isSet('mage-cache-sessid')) {
            storage.removeAll();
        }

        if (!$.localStorage.isSet('mage-customer-login')) {
            $.localStorage.set('mage-customer-login', isLoggedIn);
        }
        if ($.localStorage.get('mage-customer-login') !== isLoggedIn) {
            $.localStorage.set('mage-customer-login', isLoggedIn);
            storage.removeAll();
        }

        $.cookieStorage.set('mage-cache-sessid', true);
    };

    dataProvider = {

        /**
         * @param {Object} sectionNames
         * @return {Object}
         */
        getFromStorage: function (sectionNames) {
            var result = {};

            _.each(sectionNames, function (sectionName) {
                result[sectionName] = storage.get(sectionName);
            });

            return result;
        },

        /**
         * @param {Object} sectionNames
         * @param {Boolean} forceNewSectionTimestamp
         * @return {*}
         */
        getFromServer: function (sectionNames, forceNewSectionTimestamp) {
            var parameters;

            sectionNames = sectionConfig.filterClientSideSections(sectionNames);
            parameters = _.isArray(sectionNames) && sectionNames.indexOf('*') < 0 ? {
                sections: sectionNames.join(',')
            } : [];
            parameters['force_new_section_timestamp'] = forceNewSectionTimestamp;

            return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) {
                throw new Error(jqXHR);
            });
        }
    };

    /**
     * @param {Function} target
     * @param {String} sectionName
     * @return {*}
     */
    ko.extenders.disposableCustomerData = function (target, sectionName) {
        var sectionDataIds, newSectionDataIds = {};

        target.subscribe(function () {
            setTimeout(function () {
                storage.remove(sectionName);
                sectionDataIds = $.cookieStorage.get('section_data_ids') || {};
                _.each(sectionDataIds, function (data, name) {
                    if (name !== sectionName) {
                        newSectionDataIds[name] = data;
                    }
                });
                $.cookieStorage.set('section_data_ids', newSectionDataIds);
            }, 3000);
        });

        return target;
    };

    buffer = {
        data: {},

        /**
         * @param {String} sectionName
         */
        bind: function (sectionName) {
            this.data[sectionName] = ko.observable({});
        },

        /**
         * @param {String} sectionName
         * @return {Object}
         */
        get: function (sectionName) {
            if (!this.data[sectionName]) {
                this.bind(sectionName);
            }

            return this.data[sectionName];
        },

        /**
         * @return {Array}
         */
        keys: function () {
            return _.keys(this.data);
        },

        /**
         * @param {String} sectionName
         * @param {Object} sectionData
         */
        notify: function (sectionName, sectionData) {
            if (!this.data[sectionName]) {
                this.bind(sectionName);
            }
            this.data[sectionName](sectionData);
        },

        /**
         * @param {Object} sections
         */
        update: function (sections) {
            var sectionId = 0,
                sectionDataIds = $.cookieStorage.get('section_data_ids') || {};

            _.each(sections, function (sectionData, sectionName) {
                sectionId = sectionData['data_id'];
                sectionDataIds[sectionName] = sectionId;
                storage.set(sectionName, sectionData);
                storageInvalidation.remove(sectionName);
                buffer.notify(sectionName, sectionData);
            });
            $.cookieStorage.set('section_data_ids', sectionDataIds);
        },

        /**
         * @param {Object} sections
         */
        remove: function (sections) {
            _.each(sections, function (sectionName) {
                storage.remove(sectionName);

                if (!sectionConfig.isClientSideSection(sectionName)) {
                    storageInvalidation.set(sectionName, true);
                }
            });
        }
    };

    customerData = {

        /**
         * Customer data initialization
         */
        init: function () {
            var expiredSectionNames = this.getExpiredSectionNames();

            if (expiredSectionNames.length > 0) {
                _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
                    buffer.notify(sectionName, sectionData);
                });
                this.reload(expiredSectionNames, false);
            } else {
                _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
                    buffer.notify(sectionName, sectionData);
                });

                if (!_.isEmpty(storageInvalidation.keys())) {
                    this.reload(storageInvalidation.keys(), false);
                }
            }

            if (!_.isEmpty($.cookieStorage.get('section_data_clean'))) {
                this.reload(sectionConfig.getSectionNames(), true);
                $.cookieStorage.set('section_data_clean', '');
            }

            if (this._request) {
                this._request.done(function(){deferred.resolve()});
                return;
            }
            deferred.resolve();
        },

        /**
         * Storage init
         */
        initStorage: function () {
            $.cookieStorage.setConf({
                path: '/',
                expires: new Date(Date.now() + parseInt(options.cookieLifeTime, 10) * 1000)
            });

            if (options.cookieDomain) {
                $.cookieStorage.setConf({
                    domain: options.cookieDomain
                });
            }

            storage = $.initNamespaceStorage('mage-cache-storage').localStorage;
            storageInvalidation = $.initNamespaceStorage('mage-cache-storage-section-invalidation').localStorage;
        },

        /**
         * Retrieve the list of sections that has expired since last page reload.
         *
         * Sections can expire due to lifetime constraints or due to inconsistent storage information
         * (validated by cookie data).
         *
         * @return {Array}
         */
        getExpiredSectionNames: function () {
            var expiredSectionNames = [],
                cookieSectionTimestamps = $.cookieStorage.get('section_data_ids') || {},
                sectionLifetime = options.expirableSectionLifetime * 60,
                currentTimestamp = Math.floor(Date.now() / 1000),
                sectionData;

            // process sections that can expire due to lifetime constraints
            _.each(options.expirableSectionNames, function (sectionName) {
                sectionData = storage.get(sectionName);

                if (typeof sectionData === 'object' && sectionData['data_id'] + sectionLifetime <= currentTimestamp) {
                    expiredSectionNames.push(sectionName);
                }
            });

            // process sections that can expire due to storage information inconsistency
            _.each(cookieSectionTimestamps, function (cookieSectionTimestamp, sectionName) {
                if (storage !== undefined) {
                    sectionData = storage.get(sectionName);
                }

                if (typeof sectionData === 'undefined' ||
                    typeof sectionData === 'object' &&
                    cookieSectionTimestamp !== sectionData['data_id']
                ) {
                    expiredSectionNames.push(sectionName);
                }
            });

            //remove expired section names of previously installed/enable modules
            expiredSectionNames = _.intersection(expiredSectionNames, sectionConfig.getSectionNames());

            return _.uniq(expiredSectionNames);
        },

        /**
         * Check if some sections have to be reloaded.
         *
         * @deprecated Use getExpiredSectionNames instead.
         *
         * @return {Boolean}
         */
        needReload: function () {
            var expiredSectionNames = this.getExpiredSectionNames();

            return expiredSectionNames.length > 0;
        },

        /**
         * Retrieve the list of expired keys.
         *
         * @deprecated Use getExpiredSectionNames instead.
         *
         * @return {Array}
         */
        getExpiredKeys: function () {
            return this.getExpiredSectionNames();
        },

        /**
         * @param {String} sectionName
         * @return {*}
         */
        get: function (sectionName) {
            this._requestOnDemand(sectionName);
            return buffer.get(sectionName);
        },

        _requestOnDemand: function(sectionName) {
            if (buffer.keys().indexOf(sectionName) !== -1 || sectionName === 'messages') return;

            (this._demandList = this._demandList || []).push(sectionName);
            clearTimeout(this._demandTimeout);
            this._demandTimeout = setTimeout(function() {deferred.done(function () {
                var list = customerData._demandList.filter(function(sectionName) {
                    var data = buffer.get(sectionName)();

                    if (typeof data === 'object' && Object.keys(data).length) return false;

                    return data;
                });
                if (!list.length) return;

                customerData.reload(list, false, false);
            })}, 100);
        },

        /**
         * @param {String} sectionName
         * @param {Object} sectionData
         */
        set: function (sectionName, sectionData) {
            var data = {};

            data[sectionName] = sectionData;
            buffer.update(data);
        },

        /**
         * Avoid using this function directly 'cause of possible performance drawbacks.
         * Each customer section reload brings new non-cached ajax request.
         *
         * @param {Array} sectionNames
         * @param {Boolean} forceNewSectionTimestamp
         * @return {*}
         */
        reload: function (sectionNames, forceNewSectionTimestamp, isOverlayShown) {
            customerData._request = dataProvider.getFromServer(sectionNames, forceNewSectionTimestamp).done(function (sections) {
                $(document).trigger('customer-data-reload', [sectionNames]);
                buffer.update(sections);
            });

            if (isOverlayShown !== false) {
                require(['vaimo/overlay'], function(overlay){
                    overlay.open('customer-data');
                    customerData._request.always(function () {overlay.close('customer-data')});
                })
            }

            return customerData._request;
        },

        /**
         * @param {Array} sectionNames
         */
        invalidate: function (sectionNames) {
            var sectionDataIds,
                sectionsNamesForInvalidation;

            sectionsNamesForInvalidation = _.contains(sectionNames, '*') ? sectionConfig.getSectionNames() :
                sectionNames;

            $(document).trigger('customer-data-invalidate', [sectionsNamesForInvalidation]);
            buffer.remove(sectionsNamesForInvalidation);
            sectionDataIds = $.cookieStorage.get('section_data_ids') || {};

            // Invalidate section in cookie (increase version of section with 1000)
            _.each(sectionsNamesForInvalidation, function (sectionName) {
                if (!sectionConfig.isClientSideSection(sectionName)) {
                    sectionDataIds[sectionName] += 1000;
                }
            });
            $.cookieStorage.set('section_data_ids', sectionDataIds);
        },

        /**
         * Checks if customer data is initialized.
         *
         * @returns {jQuery.Deferred}
         */
        getInitCustomerData: function () {
            return deferred.promise();
        },

        /**
         * Reload sections on ajax complete
         *
         * @param {Object} jsonResponse
         * @param {Object} settings
         */
        onAjaxComplete: function (jsonResponse, settings) {
            var sections,
                redirects;

            if (settings.type.match(/post|put|delete/i) && !settings.isSkipCustomerDataUpdates) {
                sections = sectionConfig.getAffectedSections(settings.url);

                if (sections && sections.length) {
                    this.invalidate(sections);
                    redirects = ['redirect', 'backUrl'];

                    if (_.isObject(jsonResponse) && !_.isEmpty(_.pick(jsonResponse, redirects))) { //eslint-disable-line
                        return;
                    }
                    this.reload(sections, true);
                }
            }
        },

        /**
         * @param {Object} settings
         * @constructor
         */
        'Magento_Customer/js/customer-data': function (settings) {
            options = settings;
            customerData.initStorage();
            invalidateCacheBySessionTimeOut(settings);
            invalidateCacheByCloseCookieSession();
            customerData.init();
        }
    };

    /**
     * Events listener
     */
    $(document).on('ajaxComplete', function (event, xhr, settings) {
        customerData.onAjaxComplete(xhr.responseJSON, settings);
    });

    /**
     * Events listener
     */
    $(document).on('submit', function (event) {
        var sections;

        if (event.target.method.match(/post|put|delete/i)) {
            sections = sectionConfig.getAffectedSections(event.target.action);

            if (sections) {
                customerData.invalidate(sections);
            }
        }
    });

    return customerData;
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausCustomer/js/reload-customer-data',['Magento_Customer/js/customer-data'],customerData=>
customerData.getInitCustomerData().done(a=>
    !(customerData.get('customer')() && customerData.get('customer')().lastname) && customerData.reload([], false, false)
))
;
define('Vaimo_BauhausSe/js/mock/empty', [],()=>{});

/*! jQuery UI - v1.13.2 - 2023-02-16
* http://jqueryui.com
* Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/resizable.js, widgets/accordion.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/dialog.js, widgets/mouse.js, widgets/slider.js, widgets/tabs.js
* Copyright jQuery Foundation and other contributors; Licensed MIT */

( function( factory ) {
	"use strict";

	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define( 'Vaimo_BauhausSe/jquery/jquery-ui',[ "jquery" ], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
} )( function( $ ) {
"use strict";

$.ui = $.ui || {};

var version = $.ui.version = "1.13.2";


/*!
 * jQuery UI Widget 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Widget
//>>group: Core
//>>description: Provides a factory for creating stateful widgets with a common API.
//>>docs: http://api.jqueryui.com/jQuery.widget/
//>>demos: http://jqueryui.com/widget/


var widgetUuid = 0;
var widgetHasOwnProperty = Array.prototype.hasOwnProperty;
var widgetSlice = Array.prototype.slice;

$.cleanData = ( function( orig ) {
	return function( elems ) {
		var events, elem, i;
		for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {

			// Only trigger remove when necessary to save time
			events = $._data( elem, "events" );
			if ( events && events.remove ) {
				$( elem ).triggerHandler( "remove" );
			}
		}
		orig( elems );
	};
} )( $.cleanData );

$.widget = function( name, base, prototype ) {
	var existingConstructor, constructor, basePrototype;

	// ProxiedPrototype allows the provided prototype to remain unmodified
	// so that it can be used as a mixin for multiple widgets (#8876)
	var proxiedPrototype = {};

	var namespace = name.split( "." )[ 0 ];
	name = name.split( "." )[ 1 ];
	var fullName = namespace + "-" + name;

	if ( !prototype ) {
		prototype = base;
		base = $.Widget;
	}

	if ( Array.isArray( prototype ) ) {
		prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
	}

	// Create selector for plugin
	$.expr.pseudos[ fullName.toLowerCase() ] = function( elem ) {
		return !!$.data( elem, fullName );
	};

	$[ namespace ] = $[ namespace ] || {};
	existingConstructor = $[ namespace ][ name ];
	constructor = $[ namespace ][ name ] = function( options, element ) {

		// Allow instantiation without "new" keyword
		if ( !this || !this._createWidget ) {
			return new constructor( options, element );
		}

		// Allow instantiation without initializing for simple inheritance
		// must use "new" keyword (the code above always passes args)
		if ( arguments.length ) {
			this._createWidget( options, element );
		}
	};

	// Extend with the existing constructor to carry over any static properties
	$.extend( constructor, existingConstructor, {
		version: prototype.version,

		// Copy the object used to create the prototype in case we need to
		// redefine the widget later
		_proto: $.extend( {}, prototype ),

		// Track widgets that inherit from this widget in case this widget is
		// redefined after a widget inherits from it
		_childConstructors: []
	} );

	basePrototype = new base();

	// We need to make the options hash a property directly on the new instance
	// otherwise we'll modify the options hash on the prototype that we're
	// inheriting from
	basePrototype.options = $.widget.extend( {}, basePrototype.options );
	$.each( prototype, function( prop, value ) {
		if ( typeof value !== "function" ) {
			proxiedPrototype[ prop ] = value;
			return;
		}
		proxiedPrototype[ prop ] = ( function() {
			function _super() {
				return base.prototype[ prop ].apply( this, arguments );
			}

			function _superApply( args ) {
				return base.prototype[ prop ].apply( this, args );
			}

			return function() {
				var __super = this._super;
				var __superApply = this._superApply;
				var returnValue;

				this._super = _super;
				this._superApply = _superApply;

				returnValue = value.apply( this, arguments );

				this._super = __super;
				this._superApply = __superApply;

				return returnValue;
			};
		} )();
	} );
	constructor.prototype = $.widget.extend( basePrototype, {

		// TODO: remove support for widgetEventPrefix
		// always use the name + a colon as the prefix, e.g., draggable:start
		// don't prefix for widgets that aren't DOM-based
		widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
	}, proxiedPrototype, {
		constructor: constructor,
		namespace: namespace,
		widgetName: name,
		widgetFullName: fullName
	} );

	// If this widget is being redefined then we need to find all widgets that
	// are inheriting from it and redefine all of them so that they inherit from
	// the new version of this widget. We're essentially trying to replace one
	// level in the prototype chain.
	if ( existingConstructor ) {
		$.each( existingConstructor._childConstructors, function( i, child ) {
			var childPrototype = child.prototype;

			// Redefine the child widget using the same prototype that was
			// originally used, but inherit from the new version of the base
			$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
				child._proto );
		} );

		// Remove the list of existing child constructors from the old constructor
		// so the old child constructors can be garbage collected
		delete existingConstructor._childConstructors;
	} else {
		base._childConstructors.push( constructor );
	}

	$.widget.bridge( name, constructor );

	return constructor;
};

$.widget.extend = function( target ) {
	var input = widgetSlice.call( arguments, 1 );
	var inputIndex = 0;
	var inputLength = input.length;
	var key;
	var value;

	for ( ; inputIndex < inputLength; inputIndex++ ) {
		for ( key in input[ inputIndex ] ) {
			value = input[ inputIndex ][ key ];
			if ( widgetHasOwnProperty.call( input[ inputIndex ], key ) && value !== undefined ) {

				// Clone objects
				if ( $.isPlainObject( value ) ) {
					target[ key ] = $.isPlainObject( target[ key ] ) ?
						$.widget.extend( {}, target[ key ], value ) :

						// Don't extend strings, arrays, etc. with objects
						$.widget.extend( {}, value );

				// Copy everything else by reference
				} else {
					target[ key ] = value;
				}
			}
		}
	}
	return target;
};

$.widget.bridge = function( name, object ) {
	var fullName = object.prototype.widgetFullName || name;
	$.fn[ name ] = function( options ) {
		var isMethodCall = typeof options === "string";
		var args = widgetSlice.call( arguments, 1 );
		var returnValue = this;

		if ( isMethodCall ) {

			// If this is an empty collection, we need to have the instance method
			// return undefined instead of the jQuery instance
			if ( !this.length && options === "instance" ) {
				returnValue = undefined;
			} else {
				this.each( function() {
					var methodValue;
					var instance = $.data( this, fullName );

					if ( options === "instance" ) {
						returnValue = instance;
						return false;
					}

					if ( !instance ) {
						return $.error( "cannot call methods on " + name +
							" prior to initialization; " +
							"attempted to call method '" + options + "'" );
					}

					if ( typeof instance[ options ] !== "function" ||
						options.charAt( 0 ) === "_" ) {
						return $.error( "no such method '" + options + "' for " + name +
							" widget instance" );
					}

					methodValue = instance[ options ].apply( instance, args );

					if ( methodValue !== instance && methodValue !== undefined ) {
						returnValue = methodValue && methodValue.jquery ?
							returnValue.pushStack( methodValue.get() ) :
							methodValue;
						return false;
					}
				} );
			}
		} else {

			// Allow multiple hashes to be passed on init
			if ( args.length ) {
				options = $.widget.extend.apply( null, [ options ].concat( args ) );
			}

			this.each( function() {
				var instance = $.data( this, fullName );
				if ( instance ) {
					instance.option( options || {} );
					if ( instance._init ) {
						instance._init();
					}
				} else {
					$.data( this, fullName, new object( options, this ) );
				}
			} );
		}

		return returnValue;
	};
};

$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];

$.Widget.prototype = {
	widgetName: "widget",
	widgetEventPrefix: "",
	defaultElement: "<div>",

	options: {
		classes: {},
		disabled: false,

		// Callbacks
		create: null
	},

	_createWidget: function( options, element ) {
		element = $( element || this.defaultElement || this )[ 0 ];
		this.element = $( element );
		this.uuid = widgetUuid++;
		this.eventNamespace = "." + this.widgetName + this.uuid;

		this.bindings = $();
		this.hoverable = $();
		this.focusable = $();
		this.classesElementLookup = {};

		if ( element !== this ) {
			$.data( element, this.widgetFullName, this );
			this._on( true, this.element, {
				remove: function( event ) {
					if ( event.target === element ) {
						this.destroy();
					}
				}
			} );
			this.document = $( element.style ?

				// Element within the document
				element.ownerDocument :

				// Element is window or document
				element.document || element );
			this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
		}

		this.options = $.widget.extend( {},
			this.options,
			this._getCreateOptions(),
			options );

		this._create();

		if ( this.options.disabled ) {
			this._setOptionDisabled( this.options.disabled );
		}

		this._trigger( "create", null, this._getCreateEventData() );
		this._init();
	},

	_getCreateOptions: function() {
		return {};
	},

	_getCreateEventData: $.noop,

	_create: $.noop,

	_init: $.noop,

	destroy: function() {
		var that = this;

		this._destroy();
		$.each( this.classesElementLookup, function( key, value ) {
			that._removeClass( value, key );
		} );

		// We can probably remove the unbind calls in 2.0
		// all event bindings should go through this._on()
		this.element
			.off( this.eventNamespace )
			.removeData( this.widgetFullName );
		this.widget()
			.off( this.eventNamespace )
			.removeAttr( "aria-disabled" );

		// Clean up events and states
		this.bindings.off( this.eventNamespace );
	},

	_destroy: $.noop,

	widget: function() {
		return this.element;
	},

	option: function( key, value ) {
		var options = key;
		var parts;
		var curOption;
		var i;

		if ( arguments.length === 0 ) {

			// Don't return a reference to the internal hash
			return $.widget.extend( {}, this.options );
		}

		if ( typeof key === "string" ) {

			// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
			options = {};
			parts = key.split( "." );
			key = parts.shift();
			if ( parts.length ) {
				curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
				for ( i = 0; i < parts.length - 1; i++ ) {
					curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
					curOption = curOption[ parts[ i ] ];
				}
				key = parts.pop();
				if ( arguments.length === 1 ) {
					return curOption[ key ] === undefined ? null : curOption[ key ];
				}
				curOption[ key ] = value;
			} else {
				if ( arguments.length === 1 ) {
					return this.options[ key ] === undefined ? null : this.options[ key ];
				}
				options[ key ] = value;
			}
		}

		this._setOptions( options );

		return this;
	},

	_setOptions: function( options ) {
		var key;

		for ( key in options ) {
			this._setOption( key, options[ key ] );
		}

		return this;
	},

	_setOption: function( key, value ) {
		if ( key === "classes" ) {
			this._setOptionClasses( value );
		}

		this.options[ key ] = value;

		if ( key === "disabled" ) {
			this._setOptionDisabled( value );
		}

		return this;
	},

	_setOptionClasses: function( value ) {
		var classKey, elements, currentElements;

		for ( classKey in value ) {
			currentElements = this.classesElementLookup[ classKey ];
			if ( value[ classKey ] === this.options.classes[ classKey ] ||
					!currentElements ||
					!currentElements.length ) {
				continue;
			}

			// We are doing this to create a new jQuery object because the _removeClass() call
			// on the next line is going to destroy the reference to the current elements being
			// tracked. We need to save a copy of this collection so that we can add the new classes
			// below.
			elements = $( currentElements.get() );
			this._removeClass( currentElements, classKey );

			// We don't use _addClass() here, because that uses this.options.classes
			// for generating the string of classes. We want to use the value passed in from
			// _setOption(), this is the new value of the classes option which was passed to
			// _setOption(). We pass this value directly to _classes().
			elements.addClass( this._classes( {
				element: elements,
				keys: classKey,
				classes: value,
				add: true
			} ) );
		}
	},

	_setOptionDisabled: function( value ) {
		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );

		// If the widget is becoming disabled, then nothing is interactive
		if ( value ) {
			this._removeClass( this.hoverable, null, "ui-state-hover" );
			this._removeClass( this.focusable, null, "ui-state-focus" );
		}
	},

	enable: function() {
		return this._setOptions( { disabled: false } );
	},

	disable: function() {
		return this._setOptions( { disabled: true } );
	},

	_classes: function( options ) {
		var full = [];
		var that = this;

		options = $.extend( {
			element: this.element,
			classes: this.options.classes || {}
		}, options );

		function bindRemoveEvent() {
			var nodesToBind = [];

			options.element.each( function( _, element ) {
				var isTracked = $.map( that.classesElementLookup, function( elements ) {
					return elements;
				} )
					.some( function( elements ) {
						return elements.is( element );
					} );

				if ( !isTracked ) {
					nodesToBind.push( element );
				}
			} );

			that._on( $( nodesToBind ), {
				remove: "_untrackClassesElement"
			} );
		}

		function processClassString( classes, checkOption ) {
			var current, i;
			for ( i = 0; i < classes.length; i++ ) {
				current = that.classesElementLookup[ classes[ i ] ] || $();
				if ( options.add ) {
					bindRemoveEvent();
					current = $( $.uniqueSort( current.get().concat( options.element.get() ) ) );
				} else {
					current = $( current.not( options.element ).get() );
				}
				that.classesElementLookup[ classes[ i ] ] = current;
				full.push( classes[ i ] );
				if ( checkOption && options.classes[ classes[ i ] ] ) {
					full.push( options.classes[ classes[ i ] ] );
				}
			}
		}

		if ( options.keys ) {
			processClassString( options.keys.match( /\S+/g ) || [], true );
		}
		if ( options.extra ) {
			processClassString( options.extra.match( /\S+/g ) || [] );
		}

		return full.join( " " );
	},

	_untrackClassesElement: function( event ) {
		var that = this;
		$.each( that.classesElementLookup, function( key, value ) {
			if ( $.inArray( event.target, value ) !== -1 ) {
				that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
			}
		} );

		this._off( $( event.target ) );
	},

	_removeClass: function( element, keys, extra ) {
		return this._toggleClass( element, keys, extra, false );
	},

	_addClass: function( element, keys, extra ) {
		return this._toggleClass( element, keys, extra, true );
	},

	_toggleClass: function( element, keys, extra, add ) {
		add = ( typeof add === "boolean" ) ? add : extra;
		var shift = ( typeof element === "string" || element === null ),
			options = {
				extra: shift ? keys : extra,
				keys: shift ? element : keys,
				element: shift ? this.element : element,
				add: add
			};
		options.element.toggleClass( this._classes( options ), add );
		return this;
	},

	_on: function( suppressDisabledCheck, element, handlers ) {
		var delegateElement;
		var instance = this;

		// No suppressDisabledCheck flag, shuffle arguments
		if ( typeof suppressDisabledCheck !== "boolean" ) {
			handlers = element;
			element = suppressDisabledCheck;
			suppressDisabledCheck = false;
		}

		// No element argument, shuffle and use this.element
		if ( !handlers ) {
			handlers = element;
			element = this.element;
			delegateElement = this.widget();
		} else {
			element = delegateElement = $( element );
			this.bindings = this.bindings.add( element );
		}

		$.each( handlers, function( event, handler ) {
			function handlerProxy() {

				// Allow widgets to customize the disabled handling
				// - disabled as an array instead of boolean
				// - disabled class as method for disabling individual parts
				if ( !suppressDisabledCheck &&
						( instance.options.disabled === true ||
						$( this ).hasClass( "ui-state-disabled" ) ) ) {
					return;
				}
				return ( typeof handler === "string" ? instance[ handler ] : handler )
					.apply( instance, arguments );
			}

			// Copy the guid so direct unbinding works
			if ( typeof handler !== "string" ) {
				handlerProxy.guid = handler.guid =
					handler.guid || handlerProxy.guid || $.guid++;
			}

			var match = event.match( /^([\w:-]*)\s*(.*)$/ );
			var eventName = match[ 1 ] + instance.eventNamespace;
			var selector = match[ 2 ];

			if ( selector ) {
				delegateElement.on( eventName, selector, handlerProxy );
			} else {
				element.on( eventName, handlerProxy );
			}
		} );
	},

	_off: function( element, eventName ) {
		eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
			this.eventNamespace;
		element.off( eventName );

		// Clear the stack to avoid memory leaks (#10056)
		this.bindings = $( this.bindings.not( element ).get() );
		this.focusable = $( this.focusable.not( element ).get() );
		this.hoverable = $( this.hoverable.not( element ).get() );
	},

	_delay: function( handler, delay ) {
		function handlerProxy() {
			return ( typeof handler === "string" ? instance[ handler ] : handler )
				.apply( instance, arguments );
		}
		var instance = this;
		return setTimeout( handlerProxy, delay || 0 );
	},

	_hoverable: function( element ) {
		this.hoverable = this.hoverable.add( element );
		this._on( element, {
			mouseenter: function( event ) {
				this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
			},
			mouseleave: function( event ) {
				this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
			}
		} );
	},

	_focusable: function( element ) {
		this.focusable = this.focusable.add( element );
		this._on( element, {
			focusin: function( event ) {
				this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
			},
			focusout: function( event ) {
				this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
			}
		} );
	},

	_trigger: function( type, event, data ) {
		var prop, orig;
		var callback = this.options[ type ];

		data = data || {};
		event = $.Event( event );
		event.type = ( type === this.widgetEventPrefix ?
			type :
			this.widgetEventPrefix + type ).toLowerCase();

		// The original event may come from any element
		// so we need to reset the target on the new event
		event.target = this.element[ 0 ];

		// Copy original event properties over to the new event
		orig = event.originalEvent;
		if ( orig ) {
			for ( prop in orig ) {
				if ( !( prop in event ) ) {
					event[ prop ] = orig[ prop ];
				}
			}
		}

		this.element.trigger( event, data );
		return !( typeof callback === "function" &&
			callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
			event.isDefaultPrevented() );
	}
};

$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
	$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
		if ( typeof options === "string" ) {
			options = { effect: options };
		}

		var hasOptions;
		var effectName = !options ?
			method :
			options === true || typeof options === "number" ?
				defaultEffect :
				options.effect || defaultEffect;

		options = options || {};
		if ( typeof options === "number" ) {
			options = { duration: options };
		} else if ( options === true ) {
			options = {};
		}

		hasOptions = !$.isEmptyObject( options );
		options.complete = callback;

		if ( options.delay ) {
			element.delay( options.delay );
		}

		if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
			element[ method ]( options );
		} else if ( effectName !== method && element[ effectName ] ) {
			element[ effectName ]( options.duration, options.easing, callback );
		} else {
			element.queue( function( next ) {
				$( this )[ method ]();
				if ( callback ) {
					callback.call( element[ 0 ] );
				}
				next();
			} );
		}
	};
} );

var widget = $.widget;


/*!
 * jQuery UI Position 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/position/
 */

//>>label: Position
//>>group: Core
//>>description: Positions elements relative to other elements.
//>>docs: http://api.jqueryui.com/position/
//>>demos: http://jqueryui.com/position/


( function() {
var cachedScrollbarWidth,
	max = Math.max,
	abs = Math.abs,
	rhorizontal = /left|center|right/,
	rvertical = /top|center|bottom/,
	roffset = /[\+\-]\d+(\.[\d]+)?%?/,
	rposition = /^\w+/,
	rpercent = /%$/,
	_position = $.fn.position;

function getOffsets( offsets, width, height ) {
	return [
		parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
		parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
	];
}

function parseCss( element, property ) {
	return parseInt( $.css( element, property ), 10 ) || 0;
}

function isWindow( obj ) {
	return obj != null && obj === obj.window;
}

function getDimensions( elem ) {
	var raw = elem[ 0 ];
	if ( raw.nodeType === 9 ) {
		return {
			width: elem.width(),
			height: elem.height(),
			offset: { top: 0, left: 0 }
		};
	}
	if ( isWindow( raw ) ) {
		return {
			width: elem.width(),
			height: elem.height(),
			offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
		};
	}
	if ( raw.preventDefault ) {
		return {
			width: 0,
			height: 0,
			offset: { top: raw.pageY, left: raw.pageX }
		};
	}
	return {
		width: elem.outerWidth(),
		height: elem.outerHeight(),
		offset: elem.offset()
	};
}

$.position = {
	scrollbarWidth: function() {
		if ( cachedScrollbarWidth !== undefined ) {
			return cachedScrollbarWidth;
		}
		var w1, w2,
			div = $( "<div style=" +
				"'display:block;position:absolute;width:200px;height:200px;overflow:hidden;'>" +
				"<div style='height:300px;width:auto;'></div></div>" ),
			innerDiv = div.children()[ 0 ];

		$( "body" ).append( div );
		w1 = innerDiv.offsetWidth;
		div.css( "overflow", "scroll" );

		w2 = innerDiv.offsetWidth;

		if ( w1 === w2 ) {
			w2 = div[ 0 ].clientWidth;
		}

		div.remove();

		return ( cachedScrollbarWidth = w1 - w2 );
	},
	getScrollInfo: function( within ) {
		var overflowX = within.isWindow || within.isDocument ? "" :
				within.element.css( "overflow-x" ),
			overflowY = within.isWindow || within.isDocument ? "" :
				within.element.css( "overflow-y" ),
			hasOverflowX = overflowX === "scroll" ||
				( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
			hasOverflowY = overflowY === "scroll" ||
				( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
		return {
			width: hasOverflowY ? $.position.scrollbarWidth() : 0,
			height: hasOverflowX ? $.position.scrollbarWidth() : 0
		};
	},
	getWithinInfo: function( element ) {
		var withinElement = $( element || window ),
			isElemWindow = isWindow( withinElement[ 0 ] ),
			isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
			hasOffset = !isElemWindow && !isDocument;
		return {
			element: withinElement,
			isWindow: isElemWindow,
			isDocument: isDocument,
			offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
			scrollLeft: withinElement.scrollLeft(),
			scrollTop: withinElement.scrollTop(),
			width: withinElement.outerWidth(),
			height: withinElement.outerHeight()
		};
	}
};

$.fn.position = function( options ) {
	if ( !options || !options.of ) {
		return _position.apply( this, arguments );
	}

	// Make a copy, we don't want to modify arguments
	options = $.extend( {}, options );

	var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,

		// Make sure string options are treated as CSS selectors
		target = typeof options.of === "string" ?
			$( document ).find( options.of ) :
			$( options.of ),

		within = $.position.getWithinInfo( options.within ),
		scrollInfo = $.position.getScrollInfo( within ),
		collision = ( options.collision || "flip" ).split( " " ),
		offsets = {};

	dimensions = getDimensions( target );
	if ( target[ 0 ].preventDefault ) {

		// Force left top to allow flipping
		options.at = "left top";
	}
	targetWidth = dimensions.width;
	targetHeight = dimensions.height;
	targetOffset = dimensions.offset;

	// Clone to reuse original targetOffset later
	basePosition = $.extend( {}, targetOffset );

	// Force my and at to have valid horizontal and vertical positions
	// if a value is missing or invalid, it will be converted to center
	$.each( [ "my", "at" ], function() {
		var pos = ( options[ this ] || "" ).split( " " ),
			horizontalOffset,
			verticalOffset;

		if ( pos.length === 1 ) {
			pos = rhorizontal.test( pos[ 0 ] ) ?
				pos.concat( [ "center" ] ) :
				rvertical.test( pos[ 0 ] ) ?
					[ "center" ].concat( pos ) :
					[ "center", "center" ];
		}
		pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
		pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";

		// Calculate offsets
		horizontalOffset = roffset.exec( pos[ 0 ] );
		verticalOffset = roffset.exec( pos[ 1 ] );
		offsets[ this ] = [
			horizontalOffset ? horizontalOffset[ 0 ] : 0,
			verticalOffset ? verticalOffset[ 0 ] : 0
		];

		// Reduce to just the positions without the offsets
		options[ this ] = [
			rposition.exec( pos[ 0 ] )[ 0 ],
			rposition.exec( pos[ 1 ] )[ 0 ]
		];
	} );

	// Normalize collision option
	if ( collision.length === 1 ) {
		collision[ 1 ] = collision[ 0 ];
	}

	if ( options.at[ 0 ] === "right" ) {
		basePosition.left += targetWidth;
	} else if ( options.at[ 0 ] === "center" ) {
		basePosition.left += targetWidth / 2;
	}

	if ( options.at[ 1 ] === "bottom" ) {
		basePosition.top += targetHeight;
	} else if ( options.at[ 1 ] === "center" ) {
		basePosition.top += targetHeight / 2;
	}

	atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
	basePosition.left += atOffset[ 0 ];
	basePosition.top += atOffset[ 1 ];

	return this.each( function() {
		var collisionPosition, using,
			elem = $( this ),
			elemWidth = elem.outerWidth(),
			elemHeight = elem.outerHeight(),
			marginLeft = parseCss( this, "marginLeft" ),
			marginTop = parseCss( this, "marginTop" ),
			collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
				scrollInfo.width,
			collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
				scrollInfo.height,
			position = $.extend( {}, basePosition ),
			myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );

		if ( options.my[ 0 ] === "right" ) {
			position.left -= elemWidth;
		} else if ( options.my[ 0 ] === "center" ) {
			position.left -= elemWidth / 2;
		}

		if ( options.my[ 1 ] === "bottom" ) {
			position.top -= elemHeight;
		} else if ( options.my[ 1 ] === "center" ) {
			position.top -= elemHeight / 2;
		}

		position.left += myOffset[ 0 ];
		position.top += myOffset[ 1 ];

		collisionPosition = {
			marginLeft: marginLeft,
			marginTop: marginTop
		};

		$.each( [ "left", "top" ], function( i, dir ) {
			if ( $.ui.position[ collision[ i ] ] ) {
				$.ui.position[ collision[ i ] ][ dir ]( position, {
					targetWidth: targetWidth,
					targetHeight: targetHeight,
					elemWidth: elemWidth,
					elemHeight: elemHeight,
					collisionPosition: collisionPosition,
					collisionWidth: collisionWidth,
					collisionHeight: collisionHeight,
					offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
					my: options.my,
					at: options.at,
					within: within,
					elem: elem
				} );
			}
		} );

		if ( options.using ) {

			// Adds feedback as second argument to using callback, if present
			using = function( props ) {
				var left = targetOffset.left - position.left,
					right = left + targetWidth - elemWidth,
					top = targetOffset.top - position.top,
					bottom = top + targetHeight - elemHeight,
					feedback = {
						target: {
							element: target,
							left: targetOffset.left,
							top: targetOffset.top,
							width: targetWidth,
							height: targetHeight
						},
						element: {
							element: elem,
							left: position.left,
							top: position.top,
							width: elemWidth,
							height: elemHeight
						},
						horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
						vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
					};
				if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
					feedback.horizontal = "center";
				}
				if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
					feedback.vertical = "middle";
				}
				if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
					feedback.important = "horizontal";
				} else {
					feedback.important = "vertical";
				}
				options.using.call( this, props, feedback );
			};
		}

		elem.offset( $.extend( position, { using: using } ) );
	} );
};

$.ui.position = {
	fit: {
		left: function( position, data ) {
			var within = data.within,
				withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
				outerWidth = within.width,
				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
				overLeft = withinOffset - collisionPosLeft,
				overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
				newOverRight;

			// Element is wider than within
			if ( data.collisionWidth > outerWidth ) {

				// Element is initially over the left side of within
				if ( overLeft > 0 && overRight <= 0 ) {
					newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
						withinOffset;
					position.left += overLeft - newOverRight;

				// Element is initially over right side of within
				} else if ( overRight > 0 && overLeft <= 0 ) {
					position.left = withinOffset;

				// Element is initially over both left and right sides of within
				} else {
					if ( overLeft > overRight ) {
						position.left = withinOffset + outerWidth - data.collisionWidth;
					} else {
						position.left = withinOffset;
					}
				}

			// Too far left -> align with left edge
			} else if ( overLeft > 0 ) {
				position.left += overLeft;

			// Too far right -> align with right edge
			} else if ( overRight > 0 ) {
				position.left -= overRight;

			// Adjust based on position and margin
			} else {
				position.left = max( position.left - collisionPosLeft, position.left );
			}
		},
		top: function( position, data ) {
			var within = data.within,
				withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
				outerHeight = data.within.height,
				collisionPosTop = position.top - data.collisionPosition.marginTop,
				overTop = withinOffset - collisionPosTop,
				overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
				newOverBottom;

			// Element is taller than within
			if ( data.collisionHeight > outerHeight ) {

				// Element is initially over the top of within
				if ( overTop > 0 && overBottom <= 0 ) {
					newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
						withinOffset;
					position.top += overTop - newOverBottom;

				// Element is initially over bottom of within
				} else if ( overBottom > 0 && overTop <= 0 ) {
					position.top = withinOffset;

				// Element is initially over both top and bottom of within
				} else {
					if ( overTop > overBottom ) {
						position.top = withinOffset + outerHeight - data.collisionHeight;
					} else {
						position.top = withinOffset;
					}
				}

			// Too far up -> align with top
			} else if ( overTop > 0 ) {
				position.top += overTop;

			// Too far down -> align with bottom edge
			} else if ( overBottom > 0 ) {
				position.top -= overBottom;

			// Adjust based on position and margin
			} else {
				position.top = max( position.top - collisionPosTop, position.top );
			}
		}
	},
	flip: {
		left: function( position, data ) {
			var within = data.within,
				withinOffset = within.offset.left + within.scrollLeft,
				outerWidth = within.width,
				offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
				overLeft = collisionPosLeft - offsetLeft,
				overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
				myOffset = data.my[ 0 ] === "left" ?
					-data.elemWidth :
					data.my[ 0 ] === "right" ?
						data.elemWidth :
						0,
				atOffset = data.at[ 0 ] === "left" ?
					data.targetWidth :
					data.at[ 0 ] === "right" ?
						-data.targetWidth :
						0,
				offset = -2 * data.offset[ 0 ],
				newOverRight,
				newOverLeft;

			if ( overLeft < 0 ) {
				newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
					outerWidth - withinOffset;
				if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
					position.left += myOffset + atOffset + offset;
				}
			} else if ( overRight > 0 ) {
				newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
					atOffset + offset - offsetLeft;
				if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
					position.left += myOffset + atOffset + offset;
				}
			}
		},
		top: function( position, data ) {
			var within = data.within,
				withinOffset = within.offset.top + within.scrollTop,
				outerHeight = within.height,
				offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
				collisionPosTop = position.top - data.collisionPosition.marginTop,
				overTop = collisionPosTop - offsetTop,
				overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
				top = data.my[ 1 ] === "top",
				myOffset = top ?
					-data.elemHeight :
					data.my[ 1 ] === "bottom" ?
						data.elemHeight :
						0,
				atOffset = data.at[ 1 ] === "top" ?
					data.targetHeight :
					data.at[ 1 ] === "bottom" ?
						-data.targetHeight :
						0,
				offset = -2 * data.offset[ 1 ],
				newOverTop,
				newOverBottom;
			if ( overTop < 0 ) {
				newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
					outerHeight - withinOffset;
				if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
					position.top += myOffset + atOffset + offset;
				}
			} else if ( overBottom > 0 ) {
				newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
					offset - offsetTop;
				if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
					position.top += myOffset + atOffset + offset;
				}
			}
		}
	},
	flipfit: {
		left: function() {
			$.ui.position.flip.left.apply( this, arguments );
			$.ui.position.fit.left.apply( this, arguments );
		},
		top: function() {
			$.ui.position.flip.top.apply( this, arguments );
			$.ui.position.fit.top.apply( this, arguments );
		}
	}
};

} )();

var position = $.ui.position;


/*!
 * jQuery UI :data 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :data Selector
//>>group: Core
//>>description: Selects elements which have data stored under the specified key.
//>>docs: http://api.jqueryui.com/data-selector/


var data = $.extend( $.expr.pseudos, {
	data: $.expr.createPseudo ?
		$.expr.createPseudo( function( dataName ) {
			return function( elem ) {
				return !!$.data( elem, dataName );
			};
		} ) :

		// Support: jQuery <1.8
		function( elem, i, match ) {
			return !!$.data( elem, match[ 3 ] );
		}
} );

/*!
 * jQuery UI Disable Selection 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: disableSelection
//>>group: Core
//>>description: Disable selection of text content within the set of matched elements.
//>>docs: http://api.jqueryui.com/disableSelection/

// This file is deprecated

var disableSelection = $.fn.extend( {
	disableSelection: ( function() {
		var eventType = "onselectstart" in document.createElement( "div" ) ?
			"selectstart" :
			"mousedown";

		return function() {
			return this.on( eventType + ".ui-disableSelection", function( event ) {
				event.preventDefault();
			} );
		};
	} )(),

	enableSelection: function() {
		return this.off( ".ui-disableSelection" );
	}
} );


/*!
 * jQuery UI Focusable 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :focusable Selector
//>>group: Core
//>>description: Selects elements which can be focused.
//>>docs: http://api.jqueryui.com/focusable-selector/


// Selectors
$.ui.focusable = function( element, hasTabindex ) {
	var map, mapName, img, focusableIfVisible, fieldset,
		nodeName = element.nodeName.toLowerCase();

	if ( "area" === nodeName ) {
		map = element.parentNode;
		mapName = map.name;
		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
			return false;
		}
		img = $( "img[usemap='#" + mapName + "']" );
		return img.length > 0 && img.is( ":visible" );
	}

	if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) {
		focusableIfVisible = !element.disabled;

		if ( focusableIfVisible ) {

			// Form controls within a disabled fieldset are disabled.
			// However, controls within the fieldset's legend do not get disabled.
			// Since controls generally aren't placed inside legends, we skip
			// this portion of the check.
			fieldset = $( element ).closest( "fieldset" )[ 0 ];
			if ( fieldset ) {
				focusableIfVisible = !fieldset.disabled;
			}
		}
	} else if ( "a" === nodeName ) {
		focusableIfVisible = element.href || hasTabindex;
	} else {
		focusableIfVisible = hasTabindex;
	}

	return focusableIfVisible && $( element ).is( ":visible" ) && visible( $( element ) );
};

// Support: IE 8 only
// IE 8 doesn't resolve inherit to visible/hidden for computed values
function visible( element ) {
	var visibility = element.css( "visibility" );
	while ( visibility === "inherit" ) {
		element = element.parent();
		visibility = element.css( "visibility" );
	}
	return visibility === "visible";
}

$.extend( $.expr.pseudos, {
	focusable: function( element ) {
		return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );
	}
} );

var focusable = $.ui.focusable;



// Support: IE8 Only
// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop
// with a string, so we need to find the proper form.
var form = $.fn._form = function() {
	return typeof this[ 0 ].form === "string" ? this.closest( "form" ) : $( this[ 0 ].form );
};


/*!
 * jQuery UI Form Reset Mixin 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Form Reset Mixin
//>>group: Core
//>>description: Refresh input widgets when their form is reset
//>>docs: http://api.jqueryui.com/form-reset-mixin/


var formResetMixin = $.ui.formResetMixin = {
	_formResetHandler: function() {
		var form = $( this );

		// Wait for the form reset to actually happen before refreshing
		setTimeout( function() {
			var instances = form.data( "ui-form-reset-instances" );
			$.each( instances, function() {
				this.refresh();
			} );
		} );
	},

	_bindFormResetHandler: function() {
		this.form = this.element._form();
		if ( !this.form.length ) {
			return;
		}

		var instances = this.form.data( "ui-form-reset-instances" ) || [];
		if ( !instances.length ) {

			// We don't use _on() here because we use a single event handler per form
			this.form.on( "reset.ui-form-reset", this._formResetHandler );
		}
		instances.push( this );
		this.form.data( "ui-form-reset-instances", instances );
	},

	_unbindFormResetHandler: function() {
		if ( !this.form.length ) {
			return;
		}

		var instances = this.form.data( "ui-form-reset-instances" );
		instances.splice( $.inArray( this, instances ), 1 );
		if ( instances.length ) {
			this.form.data( "ui-form-reset-instances", instances );
		} else {
			this.form
				.removeData( "ui-form-reset-instances" )
				.off( "reset.ui-form-reset" );
		}
	}
};


/*!
 * jQuery UI Keycode 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Keycode
//>>group: Core
//>>description: Provide keycodes as keynames
//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/


var keycode = $.ui.keyCode = {
	BACKSPACE: 8,
	COMMA: 188,
	DELETE: 46,
	DOWN: 40,
	END: 35,
	ENTER: 13,
	ESCAPE: 27,
	HOME: 36,
	LEFT: 37,
	PAGE_DOWN: 34,
	PAGE_UP: 33,
	PERIOD: 190,
	RIGHT: 39,
	SPACE: 32,
	TAB: 9,
	UP: 38
};


/*!
 * jQuery UI Labels 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: labels
//>>group: Core
//>>description: Find all the labels associated with a given input
//>>docs: http://api.jqueryui.com/labels/


var labels = $.fn.labels = function() {
	var ancestor, selector, id, labels, ancestors;

	if ( !this.length ) {
		return this.pushStack( [] );
	}

	// Check control.labels first
	if ( this[ 0 ].labels && this[ 0 ].labels.length ) {
		return this.pushStack( this[ 0 ].labels );
	}

	// Support: IE <= 11, FF <= 37, Android <= 2.3 only
	// Above browsers do not support control.labels. Everything below is to support them
	// as well as document fragments. control.labels does not work on document fragments
	labels = this.eq( 0 ).parents( "label" );

	// Look for the label based on the id
	id = this.attr( "id" );
	if ( id ) {

		// We don't search against the document in case the element
		// is disconnected from the DOM
		ancestor = this.eq( 0 ).parents().last();

		// Get a full set of top level ancestors
		ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );

		// Create a selector for the label based on the id
		selector = "label[for='" + $.escapeSelector( id ) + "']";

		labels = labels.add( ancestors.find( selector ).addBack( selector ) );

	}

	// Return whatever we have found for labels
	return this.pushStack( labels );
};


/*!
 * jQuery UI Scroll Parent 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: scrollParent
//>>group: Core
//>>description: Get the closest ancestor element that is scrollable.
//>>docs: http://api.jqueryui.com/scrollParent/


var scrollParent = $.fn.scrollParent = function( includeHidden ) {
	var position = this.css( "position" ),
		excludeStaticParent = position === "absolute",
		overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
		scrollParent = this.parents().filter( function() {
			var parent = $( this );
			if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
				return false;
			}
			return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
				parent.css( "overflow-x" ) );
		} ).eq( 0 );

	return position === "fixed" || !scrollParent.length ?
		$( this[ 0 ].ownerDocument || document ) :
		scrollParent;
};


/*!
 * jQuery UI Tabbable 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :tabbable Selector
//>>group: Core
//>>description: Selects elements which can be tabbed to.
//>>docs: http://api.jqueryui.com/tabbable-selector/


var tabbable = $.extend( $.expr.pseudos, {
	tabbable: function( element ) {
		var tabIndex = $.attr( element, "tabindex" ),
			hasTabindex = tabIndex != null;
		return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex );
	}
} );


/*!
 * jQuery UI Unique ID 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: uniqueId
//>>group: Core
//>>description: Functions to generate and remove uniqueId's
//>>docs: http://api.jqueryui.com/uniqueId/


var uniqueId = $.fn.extend( {
	uniqueId: ( function() {
		var uuid = 0;

		return function() {
			return this.each( function() {
				if ( !this.id ) {
					this.id = "ui-id-" + ( ++uuid );
				}
			} );
		};
	} )(),

	removeUniqueId: function() {
		return this.each( function() {
			if ( /^ui-id-\d+$/.test( this.id ) ) {
				$( this ).removeAttr( "id" );
			}
		} );
	}
} );



// This file is deprecated
var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );

/*!
 * jQuery UI Mouse 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Mouse
//>>group: Widgets
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
//>>docs: http://api.jqueryui.com/mouse/


var mouseHandled = false;
$( document ).on( "mouseup", function() {
	mouseHandled = false;
} );

var widgetsMouse = $.widget( "ui.mouse", {
	version: "1.13.2",
	options: {
		cancel: "input, textarea, button, select, option",
		distance: 1,
		delay: 0
	},
	_mouseInit: function() {
		var that = this;

		this.element
			.on( "mousedown." + this.widgetName, function( event ) {
				return that._mouseDown( event );
			} )
			.on( "click." + this.widgetName, function( event ) {
				if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
					$.removeData( event.target, that.widgetName + ".preventClickEvent" );
					event.stopImmediatePropagation();
					return false;
				}
			} );

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	_mouseDestroy: function() {
		this.element.off( "." + this.widgetName );
		if ( this._mouseMoveDelegate ) {
			this.document
				.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
				.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
		}
	},

	_mouseDown: function( event ) {

		// don't let more than one widget handle mouseStart
		if ( mouseHandled ) {
			return;
		}

		this._mouseMoved = false;

		// We may have missed mouseup (out of window)
		if ( this._mouseStarted ) {
			this._mouseUp( event );
		}

		this._mouseDownEvent = event;

		var that = this,
			btnIsLeft = ( event.which === 1 ),

			// event.target.nodeName works around a bug in IE 8 with
			// disabled inputs (#7620)
			elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
				$( event.target ).closest( this.options.cancel ).length : false );
		if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
			return true;
		}

		this.mouseDelayMet = !this.options.delay;
		if ( !this.mouseDelayMet ) {
			this._mouseDelayTimer = setTimeout( function() {
				that.mouseDelayMet = true;
			}, this.options.delay );
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted = ( this._mouseStart( event ) !== false );
			if ( !this._mouseStarted ) {
				event.preventDefault();
				return true;
			}
		}

		// Click event may never have fired (Gecko & Opera)
		if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
			$.removeData( event.target, this.widgetName + ".preventClickEvent" );
		}

		// These delegates are required to keep context
		this._mouseMoveDelegate = function( event ) {
			return that._mouseMove( event );
		};
		this._mouseUpDelegate = function( event ) {
			return that._mouseUp( event );
		};

		this.document
			.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.on( "mouseup." + this.widgetName, this._mouseUpDelegate );

		event.preventDefault();

		mouseHandled = true;
		return true;
	},

	_mouseMove: function( event ) {

		// Only check for mouseups outside the document if you've moved inside the document
		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
		// fire a mousemove event if content is placed under the cursor. See #7778
		// Support: IE <9
		if ( this._mouseMoved ) {

			// IE mouseup check - mouseup happened when mouse was out of window
			if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
					!event.button ) {
				return this._mouseUp( event );

			// Iframe mouseup check - mouseup occurred in another document
			} else if ( !event.which ) {

				// Support: Safari <=8 - 9
				// Safari sets which to 0 if you press any of the following keys
				// during a drag (#14461)
				if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
						event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
					this.ignoreMissingWhich = true;
				} else if ( !this.ignoreMissingWhich ) {
					return this._mouseUp( event );
				}
			}
		}

		if ( event.which || event.button ) {
			this._mouseMoved = true;
		}

		if ( this._mouseStarted ) {
			this._mouseDrag( event );
			return event.preventDefault();
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted =
				( this._mouseStart( this._mouseDownEvent, event ) !== false );
			if ( this._mouseStarted ) {
				this._mouseDrag( event );
			} else {
				this._mouseUp( event );
			}
		}

		return !this._mouseStarted;
	},

	_mouseUp: function( event ) {
		this.document
			.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.off( "mouseup." + this.widgetName, this._mouseUpDelegate );

		if ( this._mouseStarted ) {
			this._mouseStarted = false;

			if ( event.target === this._mouseDownEvent.target ) {
				$.data( event.target, this.widgetName + ".preventClickEvent", true );
			}

			this._mouseStop( event );
		}

		if ( this._mouseDelayTimer ) {
			clearTimeout( this._mouseDelayTimer );
			delete this._mouseDelayTimer;
		}

		this.ignoreMissingWhich = false;
		mouseHandled = false;
		event.preventDefault();
	},

	_mouseDistanceMet: function( event ) {
		return ( Math.max(
				Math.abs( this._mouseDownEvent.pageX - event.pageX ),
				Math.abs( this._mouseDownEvent.pageY - event.pageY )
			) >= this.options.distance
		);
	},

	_mouseDelayMet: function( /* event */ ) {
		return this.mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	_mouseStart: function( /* event */ ) {},
	_mouseDrag: function( /* event */ ) {},
	_mouseStop: function( /* event */ ) {},
	_mouseCapture: function( /* event */ ) {
		return true;
	}
} );



// $.ui.plugin is deprecated. Use $.widget() extensions instead.
var plugin = $.ui.plugin = {
	add: function( module, option, set ) {
		var i,
			proto = $.ui[ module ].prototype;
		for ( i in set ) {
			proto.plugins[ i ] = proto.plugins[ i ] || [];
			proto.plugins[ i ].push( [ option, set[ i ] ] );
		}
	},
	call: function( instance, name, args, allowDisconnected ) {
		var i,
			set = instance.plugins[ name ];

		if ( !set ) {
			return;
		}

		if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||
				instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
			return;
		}

		for ( i = 0; i < set.length; i++ ) {
			if ( instance.options[ set[ i ][ 0 ] ] ) {
				set[ i ][ 1 ].apply( instance.element, args );
			}
		}
	}
};



var safeActiveElement = $.ui.safeActiveElement = function( document ) {
	var activeElement;

	// Support: IE 9 only
	// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
	try {
		activeElement = document.activeElement;
	} catch ( error ) {
		activeElement = document.body;
	}

	// Support: IE 9 - 11 only
	// IE may return null instead of an element
	// Interestingly, this only seems to occur when NOT in an iframe
	if ( !activeElement ) {
		activeElement = document.body;
	}

	// Support: IE 11 only
	// IE11 returns a seemingly empty object in some cases when accessing
	// document.activeElement from an <iframe>
	if ( !activeElement.nodeName ) {
		activeElement = document.body;
	}

	return activeElement;
};



var safeBlur = $.ui.safeBlur = function( element ) {

	// Support: IE9 - 10 only
	// If the <body> is blurred, IE will switch windows, see #9420
	if ( element && element.nodeName.toLowerCase() !== "body" ) {
		$( element ).trigger( "blur" );
	}
};


/*!
 * jQuery UI Draggable 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Draggable
//>>group: Interactions
//>>description: Enables dragging functionality for any element.
//>>docs: http://api.jqueryui.com/draggable/
//>>demos: http://jqueryui.com/draggable/
//>>css.structure: ../../themes/base/draggable.css


$.widget( "ui.draggable", $.ui.mouse, {
	version: "1.13.2",
	widgetEventPrefix: "drag",
	options: {
		addClasses: true,
		appendTo: "parent",
		axis: false,
		connectToSortable: false,
		containment: false,
		cursor: "auto",
		cursorAt: false,
		grid: false,
		handle: false,
		helper: "original",
		iframeFix: false,
		opacity: false,
		refreshPositions: false,
		revert: false,
		revertDuration: 500,
		scope: "default",
		scroll: true,
		scrollSensitivity: 20,
		scrollSpeed: 20,
		snap: false,
		snapMode: "both",
		snapTolerance: 20,
		stack: false,
		zIndex: false,

		// Callbacks
		drag: null,
		start: null,
		stop: null
	},
	_create: function() {

		if ( this.options.helper === "original" ) {
			this._setPositionRelative();
		}
		if ( this.options.addClasses ) {
			this._addClass( "ui-draggable" );
		}
		this._setHandleClassName();

		this._mouseInit();
	},

	_setOption: function( key, value ) {
		this._super( key, value );
		if ( key === "handle" ) {
			this._removeHandleClassName();
			this._setHandleClassName();
		}
	},

	_destroy: function() {
		if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
			this.destroyOnClear = true;
			return;
		}
		this._removeHandleClassName();
		this._mouseDestroy();
	},

	_mouseCapture: function( event ) {
		var o = this.options;

		// Among others, prevent a drag on a resizable-handle
		if ( this.helper || o.disabled ||
				$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
			return false;
		}

		//Quit if we're not on a valid handle
		this.handle = this._getHandle( event );
		if ( !this.handle ) {
			return false;
		}

		this._blurActiveElement( event );

		this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );

		return true;

	},

	_blockFrames: function( selector ) {
		this.iframeBlocks = this.document.find( selector ).map( function() {
			var iframe = $( this );

			return $( "<div>" )
				.css( "position", "absolute" )
				.appendTo( iframe.parent() )
				.outerWidth( iframe.outerWidth() )
				.outerHeight( iframe.outerHeight() )
				.offset( iframe.offset() )[ 0 ];
		} );
	},

	_unblockFrames: function() {
		if ( this.iframeBlocks ) {
			this.iframeBlocks.remove();
			delete this.iframeBlocks;
		}
	},

	_blurActiveElement: function( event ) {
		var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
			target = $( event.target );

		// Don't blur if the event occurred on an element that is within
		// the currently focused element
		// See #10527, #12472
		if ( target.closest( activeElement ).length ) {
			return;
		}

		// Blur any element that currently has focus, see #4261
		$.ui.safeBlur( activeElement );
	},

	_mouseStart: function( event ) {

		var o = this.options;

		//Create and append the visible helper
		this.helper = this._createHelper( event );

		this._addClass( this.helper, "ui-draggable-dragging" );

		//Cache the helper size
		this._cacheHelperProportions();

		//If ddmanager is used for droppables, set the global draggable
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.current = this;
		}

		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */

		//Cache the margins of the original element
		this._cacheMargins();

		//Store the helper's css position
		this.cssPosition = this.helper.css( "position" );
		this.scrollParent = this.helper.scrollParent( true );
		this.offsetParent = this.helper.offsetParent();
		this.hasFixedAncestor = this.helper.parents().filter( function() {
				return $( this ).css( "position" ) === "fixed";
			} ).length > 0;

		//The element's absolute position on the page minus margins
		this.positionAbs = this.element.offset();
		this._refreshOffsets( event );

		//Generate the original position
		this.originalPosition = this.position = this._generatePosition( event, false );
		this.originalPageX = event.pageX;
		this.originalPageY = event.pageY;

		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
		if ( o.cursorAt ) {
			this._adjustOffsetFromHelper( o.cursorAt );
		}

		//Set a containment if given in the options
		this._setContainment();

		//Trigger event + callbacks
		if ( this._trigger( "start", event ) === false ) {
			this._clear();
			return false;
		}

		//Recache the helper size
		this._cacheHelperProportions();

		//Prepare the droppable offsets
		if ( $.ui.ddmanager && !o.dropBehaviour ) {
			$.ui.ddmanager.prepareOffsets( this, event );
		}

		// Execute the drag once - this causes the helper not to be visible before getting its
		// correct position
		this._mouseDrag( event, true );

		// If the ddmanager is used for droppables, inform the manager that dragging has started
		// (see #5003)
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.dragStart( this, event );
		}

		return true;
	},

	_refreshOffsets: function( event ) {
		this.offset = {
			top: this.positionAbs.top - this.margins.top,
			left: this.positionAbs.left - this.margins.left,
			scroll: false,
			parent: this._getParentOffset(),
			relative: this._getRelativeOffset()
		};

		this.offset.click = {
			left: event.pageX - this.offset.left,
			top: event.pageY - this.offset.top
		};
	},

	_mouseDrag: function( event, noPropagation ) {

		// reset any necessary cached properties (see #5009)
		if ( this.hasFixedAncestor ) {
			this.offset.parent = this._getParentOffset();
		}

		//Compute the helpers position
		this.position = this._generatePosition( event, true );
		this.positionAbs = this._convertPositionTo( "absolute" );

		//Call plugins and callbacks and use the resulting position if something is returned
		if ( !noPropagation ) {
			var ui = this._uiHash();
			if ( this._trigger( "drag", event, ui ) === false ) {
				this._mouseUp( new $.Event( "mouseup", event ) );
				return false;
			}
			this.position = ui.position;
		}

		this.helper[ 0 ].style.left = this.position.left + "px";
		this.helper[ 0 ].style.top = this.position.top + "px";

		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.drag( this, event );
		}

		return false;
	},

	_mouseStop: function( event ) {

		//If we are using droppables, inform the manager about the drop
		var that = this,
			dropped = false;
		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
			dropped = $.ui.ddmanager.drop( this, event );
		}

		//if a drop comes from outside (a sortable)
		if ( this.dropped ) {
			dropped = this.dropped;
			this.dropped = false;
		}

		if ( ( this.options.revert === "invalid" && !dropped ) ||
				( this.options.revert === "valid" && dropped ) ||
				this.options.revert === true || ( typeof this.options.revert === "function" &&
				this.options.revert.call( this.element, dropped ) )
		) {
			$( this.helper ).animate(
				this.originalPosition,
				parseInt( this.options.revertDuration, 10 ),
				function() {
					if ( that._trigger( "stop", event ) !== false ) {
						that._clear();
					}
				}
			);
		} else {
			if ( this._trigger( "stop", event ) !== false ) {
				this._clear();
			}
		}

		return false;
	},

	_mouseUp: function( event ) {
		this._unblockFrames();

		// If the ddmanager is used for droppables, inform the manager that dragging has stopped
		// (see #5003)
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.dragStop( this, event );
		}

		// Only need to focus if the event occurred on the draggable itself, see #10527
		if ( this.handleElement.is( event.target ) ) {

			// The interaction is over; whether or not the click resulted in a drag,
			// focus the element
			this.element.trigger( "focus" );
		}

		return $.ui.mouse.prototype._mouseUp.call( this, event );
	},

	cancel: function() {

		if ( this.helper.is( ".ui-draggable-dragging" ) ) {
			this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
		} else {
			this._clear();
		}

		return this;

	},

	_getHandle: function( event ) {
		return this.options.handle ?
			!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
			true;
	},

	_setHandleClassName: function() {
		this.handleElement = this.options.handle ?
			this.element.find( this.options.handle ) : this.element;
		this._addClass( this.handleElement, "ui-draggable-handle" );
	},

	_removeHandleClassName: function() {
		this._removeClass( this.handleElement, "ui-draggable-handle" );
	},

	_createHelper: function( event ) {

		var o = this.options,
			helperIsFunction = typeof o.helper === "function",
			helper = helperIsFunction ?
				$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
				( o.helper === "clone" ?
					this.element.clone().removeAttr( "id" ) :
					this.element );

		if ( !helper.parents( "body" ).length ) {
			helper.appendTo( ( o.appendTo === "parent" ?
				this.element[ 0 ].parentNode :
				o.appendTo ) );
		}

		// Http://bugs.jqueryui.com/ticket/9446
		// a helper function can return the original element
		// which wouldn't have been set to relative in _create
		if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
			this._setPositionRelative();
		}

		if ( helper[ 0 ] !== this.element[ 0 ] &&
				!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
			helper.css( "position", "absolute" );
		}

		return helper;

	},

	_setPositionRelative: function() {
		if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
			this.element[ 0 ].style.position = "relative";
		}
	},

	_adjustOffsetFromHelper: function( obj ) {
		if ( typeof obj === "string" ) {
			obj = obj.split( " " );
		}
		if ( Array.isArray( obj ) ) {
			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
		}
		if ( "left" in obj ) {
			this.offset.click.left = obj.left + this.margins.left;
		}
		if ( "right" in obj ) {
			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
		}
		if ( "top" in obj ) {
			this.offset.click.top = obj.top + this.margins.top;
		}
		if ( "bottom" in obj ) {
			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
		}
	},

	_isRootNode: function( element ) {
		return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
	},

	_getParentOffset: function() {

		//Get the offsetParent and cache its position
		var po = this.offsetParent.offset(),
			document = this.document[ 0 ];

		// This is a special case where we need to modify a offset calculated on start, since the
		// following happened:
		// 1. The position of the helper is absolute, so it's position is calculated based on the
		// next positioned parent
		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
		// the document, which means that the scroll is included in the initial calculation of the
		// offset of the parent, and never recalculated upon drag
		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
			po.left += this.scrollParent.scrollLeft();
			po.top += this.scrollParent.scrollTop();
		}

		if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
			po = { top: 0, left: 0 };
		}

		return {
			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
		};

	},

	_getRelativeOffset: function() {
		if ( this.cssPosition !== "relative" ) {
			return { top: 0, left: 0 };
		}

		var p = this.element.position(),
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );

		return {
			top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
				( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
			left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
				( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
		};

	},

	_cacheMargins: function() {
		this.margins = {
			left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
			top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
			right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
			bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
		};
	},

	_cacheHelperProportions: function() {
		this.helperProportions = {
			width: this.helper.outerWidth(),
			height: this.helper.outerHeight()
		};
	},

	_setContainment: function() {

		var isUserScrollable, c, ce,
			o = this.options,
			document = this.document[ 0 ];

		this.relativeContainer = null;

		if ( !o.containment ) {
			this.containment = null;
			return;
		}

		if ( o.containment === "window" ) {
			this.containment = [
				$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
				$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
				$( window ).scrollLeft() + $( window ).width() -
					this.helperProportions.width - this.margins.left,
				$( window ).scrollTop() +
					( $( window ).height() || document.body.parentNode.scrollHeight ) -
					this.helperProportions.height - this.margins.top
			];
			return;
		}

		if ( o.containment === "document" ) {
			this.containment = [
				0,
				0,
				$( document ).width() - this.helperProportions.width - this.margins.left,
				( $( document ).height() || document.body.parentNode.scrollHeight ) -
					this.helperProportions.height - this.margins.top
			];
			return;
		}

		if ( o.containment.constructor === Array ) {
			this.containment = o.containment;
			return;
		}

		if ( o.containment === "parent" ) {
			o.containment = this.helper[ 0 ].parentNode;
		}

		c = $( o.containment );
		ce = c[ 0 ];

		if ( !ce ) {
			return;
		}

		isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );

		this.containment = [
			( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
				( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
			( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
				( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
			( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
				( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
				( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
				this.helperProportions.width -
				this.margins.left -
				this.margins.right,
			( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
				( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
				( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
				this.helperProportions.height -
				this.margins.top -
				this.margins.bottom
		];
		this.relativeContainer = c;
	},

	_convertPositionTo: function( d, pos ) {

		if ( !pos ) {
			pos = this.position;
		}

		var mod = d === "absolute" ? 1 : -1,
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );

		return {
			top: (

				// The absolute mouse position
				pos.top	+

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top * mod -
				( ( this.cssPosition === "fixed" ?
					-this.offset.scroll.top :
					( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
			),
			left: (

				// The absolute mouse position
				pos.left +

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left * mod	-
				( ( this.cssPosition === "fixed" ?
					-this.offset.scroll.left :
					( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
			)
		};

	},

	_generatePosition: function( event, constrainPosition ) {

		var containment, co, top, left,
			o = this.options,
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
			pageX = event.pageX,
			pageY = event.pageY;

		// Cache the scroll
		if ( !scrollIsRootNode || !this.offset.scroll ) {
			this.offset.scroll = {
				top: this.scrollParent.scrollTop(),
				left: this.scrollParent.scrollLeft()
			};
		}

		/*
		 * - Position constraining -
		 * Constrain the position to a mix of grid, containment.
		 */

		// If we are not dragging yet, we won't check for options
		if ( constrainPosition ) {
			if ( this.containment ) {
				if ( this.relativeContainer ) {
					co = this.relativeContainer.offset();
					containment = [
						this.containment[ 0 ] + co.left,
						this.containment[ 1 ] + co.top,
						this.containment[ 2 ] + co.left,
						this.containment[ 3 ] + co.top
					];
				} else {
					containment = this.containment;
				}

				if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
					pageX = containment[ 0 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
					pageY = containment[ 1 ] + this.offset.click.top;
				}
				if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
					pageX = containment[ 2 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
					pageY = containment[ 3 ] + this.offset.click.top;
				}
			}

			if ( o.grid ) {

				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
				// argument errors in IE (see ticket #6950)
				top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
					this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
				pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
					top - this.offset.click.top > containment[ 3 ] ) ?
						top :
						( ( top - this.offset.click.top >= containment[ 1 ] ) ?
							top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;

				left = o.grid[ 0 ] ? this.originalPageX +
					Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
					this.originalPageX;
				pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
					left - this.offset.click.left > containment[ 2 ] ) ?
						left :
						( ( left - this.offset.click.left >= containment[ 0 ] ) ?
							left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
			}

			if ( o.axis === "y" ) {
				pageX = this.originalPageX;
			}

			if ( o.axis === "x" ) {
				pageY = this.originalPageY;
			}
		}

		return {
			top: (

				// The absolute mouse position
				pageY -

				// Click offset (relative to the element)
				this.offset.click.top -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top +
				( this.cssPosition === "fixed" ?
					-this.offset.scroll.top :
					( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
			),
			left: (

				// The absolute mouse position
				pageX -

				// Click offset (relative to the element)
				this.offset.click.left -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left +
				( this.cssPosition === "fixed" ?
					-this.offset.scroll.left :
					( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
			)
		};

	},

	_clear: function() {
		this._removeClass( this.helper, "ui-draggable-dragging" );
		if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
			this.helper.remove();
		}
		this.helper = null;
		this.cancelHelperRemoval = false;
		if ( this.destroyOnClear ) {
			this.destroy();
		}
	},

	// From now on bulk stuff - mainly helpers

	_trigger: function( type, event, ui ) {
		ui = ui || this._uiHash();
		$.ui.plugin.call( this, type, [ event, ui, this ], true );

		// Absolute position and offset (see #6884 ) have to be recalculated after plugins
		if ( /^(drag|start|stop)/.test( type ) ) {
			this.positionAbs = this._convertPositionTo( "absolute" );
			ui.offset = this.positionAbs;
		}
		return $.Widget.prototype._trigger.call( this, type, event, ui );
	},

	plugins: {},

	_uiHash: function() {
		return {
			helper: this.helper,
			position: this.position,
			originalPosition: this.originalPosition,
			offset: this.positionAbs
		};
	}

} );

$.ui.plugin.add( "draggable", "connectToSortable", {
	start: function( event, ui, draggable ) {
		var uiSortable = $.extend( {}, ui, {
			item: draggable.element
		} );

		draggable.sortables = [];
		$( draggable.options.connectToSortable ).each( function() {
			var sortable = $( this ).sortable( "instance" );

			if ( sortable && !sortable.options.disabled ) {
				draggable.sortables.push( sortable );

				// RefreshPositions is called at drag start to refresh the containerCache
				// which is used in drag. This ensures it's initialized and synchronized
				// with any changes that might have happened on the page since initialization.
				sortable.refreshPositions();
				sortable._trigger( "activate", event, uiSortable );
			}
		} );
	},
	stop: function( event, ui, draggable ) {
		var uiSortable = $.extend( {}, ui, {
			item: draggable.element
		} );

		draggable.cancelHelperRemoval = false;

		$.each( draggable.sortables, function() {
			var sortable = this;

			if ( sortable.isOver ) {
				sortable.isOver = 0;

				// Allow this sortable to handle removing the helper
				draggable.cancelHelperRemoval = true;
				sortable.cancelHelperRemoval = false;

				// Use _storedCSS To restore properties in the sortable,
				// as this also handles revert (#9675) since the draggable
				// may have modified them in unexpected ways (#8809)
				sortable._storedCSS = {
					position: sortable.placeholder.css( "position" ),
					top: sortable.placeholder.css( "top" ),
					left: sortable.placeholder.css( "left" )
				};

				sortable._mouseStop( event );

				// Once drag has ended, the sortable should return to using
				// its original helper, not the shared helper from draggable
				sortable.options.helper = sortable.options._helper;
			} else {

				// Prevent this Sortable from removing the helper.
				// However, don't set the draggable to remove the helper
				// either as another connected Sortable may yet handle the removal.
				sortable.cancelHelperRemoval = true;

				sortable._trigger( "deactivate", event, uiSortable );
			}
		} );
	},
	drag: function( event, ui, draggable ) {
		$.each( draggable.sortables, function() {
			var innermostIntersecting = false,
				sortable = this;

			// Copy over variables that sortable's _intersectsWith uses
			sortable.positionAbs = draggable.positionAbs;
			sortable.helperProportions = draggable.helperProportions;
			sortable.offset.click = draggable.offset.click;

			if ( sortable._intersectsWith( sortable.containerCache ) ) {
				innermostIntersecting = true;

				$.each( draggable.sortables, function() {

					// Copy over variables that sortable's _intersectsWith uses
					this.positionAbs = draggable.positionAbs;
					this.helperProportions = draggable.helperProportions;
					this.offset.click = draggable.offset.click;

					if ( this !== sortable &&
							this._intersectsWith( this.containerCache ) &&
							$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
						innermostIntersecting = false;
					}

					return innermostIntersecting;
				} );
			}

			if ( innermostIntersecting ) {

				// If it intersects, we use a little isOver variable and set it once,
				// so that the move-in stuff gets fired only once.
				if ( !sortable.isOver ) {
					sortable.isOver = 1;

					// Store draggable's parent in case we need to reappend to it later.
					draggable._parent = ui.helper.parent();

					sortable.currentItem = ui.helper
						.appendTo( sortable.element )
						.data( "ui-sortable-item", true );

					// Store helper option to later restore it
					sortable.options._helper = sortable.options.helper;

					sortable.options.helper = function() {
						return ui.helper[ 0 ];
					};

					// Fire the start events of the sortable with our passed browser event,
					// and our own helper (so it doesn't create a new one)
					event.target = sortable.currentItem[ 0 ];
					sortable._mouseCapture( event, true );
					sortable._mouseStart( event, true, true );

					// Because the browser event is way off the new appended portlet,
					// modify necessary variables to reflect the changes
					sortable.offset.click.top = draggable.offset.click.top;
					sortable.offset.click.left = draggable.offset.click.left;
					sortable.offset.parent.left -= draggable.offset.parent.left -
						sortable.offset.parent.left;
					sortable.offset.parent.top -= draggable.offset.parent.top -
						sortable.offset.parent.top;

					draggable._trigger( "toSortable", event );

					// Inform draggable that the helper is in a valid drop zone,
					// used solely in the revert option to handle "valid/invalid".
					draggable.dropped = sortable.element;

					// Need to refreshPositions of all sortables in the case that
					// adding to one sortable changes the location of the other sortables (#9675)
					$.each( draggable.sortables, function() {
						this.refreshPositions();
					} );

					// Hack so receive/update callbacks work (mostly)
					draggable.currentItem = draggable.element;
					sortable.fromOutside = draggable;
				}

				if ( sortable.currentItem ) {
					sortable._mouseDrag( event );

					// Copy the sortable's position because the draggable's can potentially reflect
					// a relative position, while sortable is always absolute, which the dragged
					// element has now become. (#8809)
					ui.position = sortable.position;
				}
			} else {

				// If it doesn't intersect with the sortable, and it intersected before,
				// we fake the drag stop of the sortable, but make sure it doesn't remove
				// the helper by using cancelHelperRemoval.
				if ( sortable.isOver ) {

					sortable.isOver = 0;
					sortable.cancelHelperRemoval = true;

					// Calling sortable's mouseStop would trigger a revert,
					// so revert must be temporarily false until after mouseStop is called.
					sortable.options._revert = sortable.options.revert;
					sortable.options.revert = false;

					sortable._trigger( "out", event, sortable._uiHash( sortable ) );
					sortable._mouseStop( event, true );

					// Restore sortable behaviors that were modfied
					// when the draggable entered the sortable area (#9481)
					sortable.options.revert = sortable.options._revert;
					sortable.options.helper = sortable.options._helper;

					if ( sortable.placeholder ) {
						sortable.placeholder.remove();
					}

					// Restore and recalculate the draggable's offset considering the sortable
					// may have modified them in unexpected ways. (#8809, #10669)
					ui.helper.appendTo( draggable._parent );
					draggable._refreshOffsets( event );
					ui.position = draggable._generatePosition( event, true );

					draggable._trigger( "fromSortable", event );

					// Inform draggable that the helper is no longer in a valid drop zone
					draggable.dropped = false;

					// Need to refreshPositions of all sortables just in case removing
					// from one sortable changes the location of other sortables (#9675)
					$.each( draggable.sortables, function() {
						this.refreshPositions();
					} );
				}
			}
		} );
	}
} );

$.ui.plugin.add( "draggable", "cursor", {
	start: function( event, ui, instance ) {
		var t = $( "body" ),
			o = instance.options;

		if ( t.css( "cursor" ) ) {
			o._cursor = t.css( "cursor" );
		}
		t.css( "cursor", o.cursor );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;
		if ( o._cursor ) {
			$( "body" ).css( "cursor", o._cursor );
		}
	}
} );

$.ui.plugin.add( "draggable", "opacity", {
	start: function( event, ui, instance ) {
		var t = $( ui.helper ),
			o = instance.options;
		if ( t.css( "opacity" ) ) {
			o._opacity = t.css( "opacity" );
		}
		t.css( "opacity", o.opacity );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;
		if ( o._opacity ) {
			$( ui.helper ).css( "opacity", o._opacity );
		}
	}
} );

$.ui.plugin.add( "draggable", "scroll", {
	start: function( event, ui, i ) {
		if ( !i.scrollParentNotHidden ) {
			i.scrollParentNotHidden = i.helper.scrollParent( false );
		}

		if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
				i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
			i.overflowOffset = i.scrollParentNotHidden.offset();
		}
	},
	drag: function( event, ui, i  ) {

		var o = i.options,
			scrolled = false,
			scrollParent = i.scrollParentNotHidden[ 0 ],
			document = i.document[ 0 ];

		if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
			if ( !o.axis || o.axis !== "x" ) {
				if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
						o.scrollSensitivity ) {
					scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
				} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
					scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
				}
			}

			if ( !o.axis || o.axis !== "y" ) {
				if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
						o.scrollSensitivity ) {
					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
				} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
				}
			}

		} else {

			if ( !o.axis || o.axis !== "x" ) {
				if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
					scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
				} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
						o.scrollSensitivity ) {
					scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
				}
			}

			if ( !o.axis || o.axis !== "y" ) {
				if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
					scrolled = $( document ).scrollLeft(
						$( document ).scrollLeft() - o.scrollSpeed
					);
				} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
						o.scrollSensitivity ) {
					scrolled = $( document ).scrollLeft(
						$( document ).scrollLeft() + o.scrollSpeed
					);
				}
			}

		}

		if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
			$.ui.ddmanager.prepareOffsets( i, event );
		}

	}
} );

$.ui.plugin.add( "draggable", "snap", {
	start: function( event, ui, i ) {

		var o = i.options;

		i.snapElements = [];

		$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
			.each( function() {
				var $t = $( this ),
					$o = $t.offset();
				if ( this !== i.element[ 0 ] ) {
					i.snapElements.push( {
						item: this,
						width: $t.outerWidth(), height: $t.outerHeight(),
						top: $o.top, left: $o.left
					} );
				}
			} );

	},
	drag: function( event, ui, inst ) {

		var ts, bs, ls, rs, l, r, t, b, i, first,
			o = inst.options,
			d = o.snapTolerance,
			x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;

		for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {

			l = inst.snapElements[ i ].left - inst.margins.left;
			r = l + inst.snapElements[ i ].width;
			t = inst.snapElements[ i ].top - inst.margins.top;
			b = t + inst.snapElements[ i ].height;

			if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
					!$.contains( inst.snapElements[ i ].item.ownerDocument,
					inst.snapElements[ i ].item ) ) {
				if ( inst.snapElements[ i ].snapping ) {
					if ( inst.options.snap.release ) {
						inst.options.snap.release.call(
							inst.element,
							event,
							$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
						);
					}
				}
				inst.snapElements[ i ].snapping = false;
				continue;
			}

			if ( o.snapMode !== "inner" ) {
				ts = Math.abs( t - y2 ) <= d;
				bs = Math.abs( b - y1 ) <= d;
				ls = Math.abs( l - x2 ) <= d;
				rs = Math.abs( r - x1 ) <= d;
				if ( ts ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: t - inst.helperProportions.height,
						left: 0
					} ).top;
				}
				if ( bs ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: b,
						left: 0
					} ).top;
				}
				if ( ls ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: l - inst.helperProportions.width
					} ).left;
				}
				if ( rs ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: r
					} ).left;
				}
			}

			first = ( ts || bs || ls || rs );

			if ( o.snapMode !== "outer" ) {
				ts = Math.abs( t - y1 ) <= d;
				bs = Math.abs( b - y2 ) <= d;
				ls = Math.abs( l - x1 ) <= d;
				rs = Math.abs( r - x2 ) <= d;
				if ( ts ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: t,
						left: 0
					} ).top;
				}
				if ( bs ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: b - inst.helperProportions.height,
						left: 0
					} ).top;
				}
				if ( ls ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: l
					} ).left;
				}
				if ( rs ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: r - inst.helperProportions.width
					} ).left;
				}
			}

			if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
				if ( inst.options.snap.snap ) {
					inst.options.snap.snap.call(
						inst.element,
						event,
						$.extend( inst._uiHash(), {
							snapItem: inst.snapElements[ i ].item
						} ) );
				}
			}
			inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );

		}

	}
} );

$.ui.plugin.add( "draggable", "stack", {
	start: function( event, ui, instance ) {
		var min,
			o = instance.options,
			group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
				return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
					( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
			} );

		if ( !group.length ) {
			return;
		}

		min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
		$( group ).each( function( i ) {
			$( this ).css( "zIndex", min + i );
		} );
		this.css( "zIndex", ( min + group.length ) );
	}
} );

$.ui.plugin.add( "draggable", "zIndex", {
	start: function( event, ui, instance ) {
		var t = $( ui.helper ),
			o = instance.options;

		if ( t.css( "zIndex" ) ) {
			o._zIndex = t.css( "zIndex" );
		}
		t.css( "zIndex", o.zIndex );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;

		if ( o._zIndex ) {
			$( ui.helper ).css( "zIndex", o._zIndex );
		}
	}
} );

var widgetsDraggable = $.ui.draggable;


/*!
 * jQuery UI Resizable 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Resizable
//>>group: Interactions
//>>description: Enables resize functionality for any element.
//>>docs: http://api.jqueryui.com/resizable/
//>>demos: http://jqueryui.com/resizable/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/resizable.css
//>>css.theme: ../../themes/base/theme.css


$.widget( "ui.resizable", $.ui.mouse, {
	version: "1.13.2",
	widgetEventPrefix: "resize",
	options: {
		alsoResize: false,
		animate: false,
		animateDuration: "slow",
		animateEasing: "swing",
		aspectRatio: false,
		autoHide: false,
		classes: {
			"ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se"
		},
		containment: false,
		ghost: false,
		grid: false,
		handles: "e,s,se",
		helper: false,
		maxHeight: null,
		maxWidth: null,
		minHeight: 10,
		minWidth: 10,

		// See #7960
		zIndex: 90,

		// Callbacks
		resize: null,
		start: null,
		stop: null
	},

	_num: function( value ) {
		return parseFloat( value ) || 0;
	},

	_isNumber: function( value ) {
		return !isNaN( parseFloat( value ) );
	},

	_hasScroll: function( el, a ) {

		if ( $( el ).css( "overflow" ) === "hidden" ) {
			return false;
		}

		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
			has = false;

		if ( el[ scroll ] > 0 ) {
			return true;
		}

		// TODO: determine which cases actually cause this to happen
		// if the element doesn't have the scroll set, see if it's possible to
		// set the scroll
		try {
			el[ scroll ] = 1;
			has = ( el[ scroll ] > 0 );
			el[ scroll ] = 0;
		} catch ( e ) {

			// `el` might be a string, then setting `scroll` will throw
			// an error in strict mode; ignore it.
		}
		return has;
	},

	_create: function() {

		var margins,
			o = this.options,
			that = this;
		this._addClass( "ui-resizable" );

		$.extend( this, {
			_aspectRatio: !!( o.aspectRatio ),
			aspectRatio: o.aspectRatio,
			originalElement: this.element,
			_proportionallyResizeElements: [],
			_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
		} );

		// Wrap the element if it cannot hold child nodes
		if ( this.element[ 0 ].nodeName.match( /^(canvas|textarea|input|select|button|img)$/i ) ) {

			this.element.wrap(
				$( "<div class='ui-wrapper'></div>" ).css( {
					overflow: "hidden",
					position: this.element.css( "position" ),
					width: this.element.outerWidth(),
					height: this.element.outerHeight(),
					top: this.element.css( "top" ),
					left: this.element.css( "left" )
				} )
			);

			this.element = this.element.parent().data(
				"ui-resizable", this.element.resizable( "instance" )
			);

			this.elementIsWrapper = true;

			margins = {
				marginTop: this.originalElement.css( "marginTop" ),
				marginRight: this.originalElement.css( "marginRight" ),
				marginBottom: this.originalElement.css( "marginBottom" ),
				marginLeft: this.originalElement.css( "marginLeft" )
			};

			this.element.css( margins );
			this.originalElement.css( "margin", 0 );

			// support: Safari
			// Prevent Safari textarea resize
			this.originalResizeStyle = this.originalElement.css( "resize" );
			this.originalElement.css( "resize", "none" );

			this._proportionallyResizeElements.push( this.originalElement.css( {
				position: "static",
				zoom: 1,
				display: "block"
			} ) );

			// Support: IE9
			// avoid IE jump (hard set the margin)
			this.originalElement.css( margins );

			this._proportionallyResize();
		}

		this._setupHandles();

		if ( o.autoHide ) {
			$( this.element )
				.on( "mouseenter", function() {
					if ( o.disabled ) {
						return;
					}
					that._removeClass( "ui-resizable-autohide" );
					that._handles.show();
				} )
				.on( "mouseleave", function() {
					if ( o.disabled ) {
						return;
					}
					if ( !that.resizing ) {
						that._addClass( "ui-resizable-autohide" );
						that._handles.hide();
					}
				} );
		}

		this._mouseInit();
	},

	_destroy: function() {

		this._mouseDestroy();
		this._addedHandles.remove();

		var wrapper,
			_destroy = function( exp ) {
				$( exp )
					.removeData( "resizable" )
					.removeData( "ui-resizable" )
					.off( ".resizable" );
			};

		// TODO: Unwrap at same DOM position
		if ( this.elementIsWrapper ) {
			_destroy( this.element );
			wrapper = this.element;
			this.originalElement.css( {
				position: wrapper.css( "position" ),
				width: wrapper.outerWidth(),
				height: wrapper.outerHeight(),
				top: wrapper.css( "top" ),
				left: wrapper.css( "left" )
			} ).insertAfter( wrapper );
			wrapper.remove();
		}

		this.originalElement.css( "resize", this.originalResizeStyle );
		_destroy( this.originalElement );

		return this;
	},

	_setOption: function( key, value ) {
		this._super( key, value );

		switch ( key ) {
		case "handles":
			this._removeHandles();
			this._setupHandles();
			break;
		case "aspectRatio":
			this._aspectRatio = !!value;
			break;
		default:
			break;
		}
	},

	_setupHandles: function() {
		var o = this.options, handle, i, n, hname, axis, that = this;
		this.handles = o.handles ||
			( !$( ".ui-resizable-handle", this.element ).length ?
				"e,s,se" : {
					n: ".ui-resizable-n",
					e: ".ui-resizable-e",
					s: ".ui-resizable-s",
					w: ".ui-resizable-w",
					se: ".ui-resizable-se",
					sw: ".ui-resizable-sw",
					ne: ".ui-resizable-ne",
					nw: ".ui-resizable-nw"
				} );

		this._handles = $();
		this._addedHandles = $();
		if ( this.handles.constructor === String ) {

			if ( this.handles === "all" ) {
				this.handles = "n,e,s,w,se,sw,ne,nw";
			}

			n = this.handles.split( "," );
			this.handles = {};

			for ( i = 0; i < n.length; i++ ) {

				handle = String.prototype.trim.call( n[ i ] );
				hname = "ui-resizable-" + handle;
				axis = $( "<div>" );
				this._addClass( axis, "ui-resizable-handle " + hname );

				axis.css( { zIndex: o.zIndex } );

				this.handles[ handle ] = ".ui-resizable-" + handle;
				if ( !this.element.children( this.handles[ handle ] ).length ) {
					this.element.append( axis );
					this._addedHandles = this._addedHandles.add( axis );
				}
			}

		}

		this._renderAxis = function( target ) {

			var i, axis, padPos, padWrapper;

			target = target || this.element;

			for ( i in this.handles ) {

				if ( this.handles[ i ].constructor === String ) {
					this.handles[ i ] = this.element.children( this.handles[ i ] ).first().show();
				} else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
					this.handles[ i ] = $( this.handles[ i ] );
					this._on( this.handles[ i ], { "mousedown": that._mouseDown } );
				}

				if ( this.elementIsWrapper &&
						this.originalElement[ 0 ]
							.nodeName
							.match( /^(textarea|input|select|button)$/i ) ) {
					axis = $( this.handles[ i ], this.element );

					padWrapper = /sw|ne|nw|se|n|s/.test( i ) ?
						axis.outerHeight() :
						axis.outerWidth();

					padPos = [ "padding",
						/ne|nw|n/.test( i ) ? "Top" :
						/se|sw|s/.test( i ) ? "Bottom" :
						/^e$/.test( i ) ? "Right" : "Left" ].join( "" );

					target.css( padPos, padWrapper );

					this._proportionallyResize();
				}

				this._handles = this._handles.add( this.handles[ i ] );
			}
		};

		// TODO: make renderAxis a prototype function
		this._renderAxis( this.element );

		this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
		this._handles.disableSelection();

		this._handles.on( "mouseover", function() {
			if ( !that.resizing ) {
				if ( this.className ) {
					axis = this.className.match( /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i );
				}
				that.axis = axis && axis[ 1 ] ? axis[ 1 ] : "se";
			}
		} );

		if ( o.autoHide ) {
			this._handles.hide();
			this._addClass( "ui-resizable-autohide" );
		}
	},

	_removeHandles: function() {
		this._addedHandles.remove();
	},

	_mouseCapture: function( event ) {
		var i, handle,
			capture = false;

		for ( i in this.handles ) {
			handle = $( this.handles[ i ] )[ 0 ];
			if ( handle === event.target || $.contains( handle, event.target ) ) {
				capture = true;
			}
		}

		return !this.options.disabled && capture;
	},

	_mouseStart: function( event ) {

		var curleft, curtop, cursor,
			o = this.options,
			el = this.element;

		this.resizing = true;

		this._renderProxy();

		curleft = this._num( this.helper.css( "left" ) );
		curtop = this._num( this.helper.css( "top" ) );

		if ( o.containment ) {
			curleft += $( o.containment ).scrollLeft() || 0;
			curtop += $( o.containment ).scrollTop() || 0;
		}

		this.offset = this.helper.offset();
		this.position = { left: curleft, top: curtop };

		this.size = this._helper ? {
				width: this.helper.width(),
				height: this.helper.height()
			} : {
				width: el.width(),
				height: el.height()
			};

		this.originalSize = this._helper ? {
				width: el.outerWidth(),
				height: el.outerHeight()
			} : {
				width: el.width(),
				height: el.height()
			};

		this.sizeDiff = {
			width: el.outerWidth() - el.width(),
			height: el.outerHeight() - el.height()
		};

		this.originalPosition = { left: curleft, top: curtop };
		this.originalMousePosition = { left: event.pageX, top: event.pageY };

		this.aspectRatio = ( typeof o.aspectRatio === "number" ) ?
			o.aspectRatio :
			( ( this.originalSize.width / this.originalSize.height ) || 1 );

		cursor = $( ".ui-resizable-" + this.axis ).css( "cursor" );
		$( "body" ).css( "cursor", cursor === "auto" ? this.axis + "-resize" : cursor );

		this._addClass( "ui-resizable-resizing" );
		this._propagate( "start", event );
		return true;
	},

	_mouseDrag: function( event ) {

		var data, props,
			smp = this.originalMousePosition,
			a = this.axis,
			dx = ( event.pageX - smp.left ) || 0,
			dy = ( event.pageY - smp.top ) || 0,
			trigger = this._change[ a ];

		this._updatePrevProperties();

		if ( !trigger ) {
			return false;
		}

		data = trigger.apply( this, [ event, dx, dy ] );

		this._updateVirtualBoundaries( event.shiftKey );
		if ( this._aspectRatio || event.shiftKey ) {
			data = this._updateRatio( data, event );
		}

		data = this._respectSize( data, event );

		this._updateCache( data );

		this._propagate( "resize", event );

		props = this._applyChanges();

		if ( !this._helper && this._proportionallyResizeElements.length ) {
			this._proportionallyResize();
		}

		if ( !$.isEmptyObject( props ) ) {
			this._updatePrevProperties();
			this._trigger( "resize", event, this.ui() );
			this._applyChanges();
		}

		return false;
	},

	_mouseStop: function( event ) {

		this.resizing = false;
		var pr, ista, soffseth, soffsetw, s, left, top,
			o = this.options, that = this;

		if ( this._helper ) {

			pr = this._proportionallyResizeElements;
			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName );
			soffseth = ista && this._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height;
			soffsetw = ista ? 0 : that.sizeDiff.width;

			s = {
				width: ( that.helper.width()  - soffsetw ),
				height: ( that.helper.height() - soffseth )
			};
			left = ( parseFloat( that.element.css( "left" ) ) +
				( that.position.left - that.originalPosition.left ) ) || null;
			top = ( parseFloat( that.element.css( "top" ) ) +
				( that.position.top - that.originalPosition.top ) ) || null;

			if ( !o.animate ) {
				this.element.css( $.extend( s, { top: top, left: left } ) );
			}

			that.helper.height( that.size.height );
			that.helper.width( that.size.width );

			if ( this._helper && !o.animate ) {
				this._proportionallyResize();
			}
		}

		$( "body" ).css( "cursor", "auto" );

		this._removeClass( "ui-resizable-resizing" );

		this._propagate( "stop", event );

		if ( this._helper ) {
			this.helper.remove();
		}

		return false;

	},

	_updatePrevProperties: function() {
		this.prevPosition = {
			top: this.position.top,
			left: this.position.left
		};
		this.prevSize = {
			width: this.size.width,
			height: this.size.height
		};
	},

	_applyChanges: function() {
		var props = {};

		if ( this.position.top !== this.prevPosition.top ) {
			props.top = this.position.top + "px";
		}
		if ( this.position.left !== this.prevPosition.left ) {
			props.left = this.position.left + "px";
		}
		if ( this.size.width !== this.prevSize.width ) {
			props.width = this.size.width + "px";
		}
		if ( this.size.height !== this.prevSize.height ) {
			props.height = this.size.height + "px";
		}

		this.helper.css( props );

		return props;
	},

	_updateVirtualBoundaries: function( forceAspectRatio ) {
		var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
			o = this.options;

		b = {
			minWidth: this._isNumber( o.minWidth ) ? o.minWidth : 0,
			maxWidth: this._isNumber( o.maxWidth ) ? o.maxWidth : Infinity,
			minHeight: this._isNumber( o.minHeight ) ? o.minHeight : 0,
			maxHeight: this._isNumber( o.maxHeight ) ? o.maxHeight : Infinity
		};

		if ( this._aspectRatio || forceAspectRatio ) {
			pMinWidth = b.minHeight * this.aspectRatio;
			pMinHeight = b.minWidth / this.aspectRatio;
			pMaxWidth = b.maxHeight * this.aspectRatio;
			pMaxHeight = b.maxWidth / this.aspectRatio;

			if ( pMinWidth > b.minWidth ) {
				b.minWidth = pMinWidth;
			}
			if ( pMinHeight > b.minHeight ) {
				b.minHeight = pMinHeight;
			}
			if ( pMaxWidth < b.maxWidth ) {
				b.maxWidth = pMaxWidth;
			}
			if ( pMaxHeight < b.maxHeight ) {
				b.maxHeight = pMaxHeight;
			}
		}
		this._vBoundaries = b;
	},

	_updateCache: function( data ) {
		this.offset = this.helper.offset();
		if ( this._isNumber( data.left ) ) {
			this.position.left = data.left;
		}
		if ( this._isNumber( data.top ) ) {
			this.position.top = data.top;
		}
		if ( this._isNumber( data.height ) ) {
			this.size.height = data.height;
		}
		if ( this._isNumber( data.width ) ) {
			this.size.width = data.width;
		}
	},

	_updateRatio: function( data ) {

		var cpos = this.position,
			csize = this.size,
			a = this.axis;

		if ( this._isNumber( data.height ) ) {
			data.width = ( data.height * this.aspectRatio );
		} else if ( this._isNumber( data.width ) ) {
			data.height = ( data.width / this.aspectRatio );
		}

		if ( a === "sw" ) {
			data.left = cpos.left + ( csize.width - data.width );
			data.top = null;
		}
		if ( a === "nw" ) {
			data.top = cpos.top + ( csize.height - data.height );
			data.left = cpos.left + ( csize.width - data.width );
		}

		return data;
	},

	_respectSize: function( data ) {

		var o = this._vBoundaries,
			a = this.axis,
			ismaxw = this._isNumber( data.width ) && o.maxWidth && ( o.maxWidth < data.width ),
			ismaxh = this._isNumber( data.height ) && o.maxHeight && ( o.maxHeight < data.height ),
			isminw = this._isNumber( data.width ) && o.minWidth && ( o.minWidth > data.width ),
			isminh = this._isNumber( data.height ) && o.minHeight && ( o.minHeight > data.height ),
			dw = this.originalPosition.left + this.originalSize.width,
			dh = this.originalPosition.top + this.originalSize.height,
			cw = /sw|nw|w/.test( a ), ch = /nw|ne|n/.test( a );
		if ( isminw ) {
			data.width = o.minWidth;
		}
		if ( isminh ) {
			data.height = o.minHeight;
		}
		if ( ismaxw ) {
			data.width = o.maxWidth;
		}
		if ( ismaxh ) {
			data.height = o.maxHeight;
		}

		if ( isminw && cw ) {
			data.left = dw - o.minWidth;
		}
		if ( ismaxw && cw ) {
			data.left = dw - o.maxWidth;
		}
		if ( isminh && ch ) {
			data.top = dh - o.minHeight;
		}
		if ( ismaxh && ch ) {
			data.top = dh - o.maxHeight;
		}

		// Fixing jump error on top/left - bug #2330
		if ( !data.width && !data.height && !data.left && data.top ) {
			data.top = null;
		} else if ( !data.width && !data.height && !data.top && data.left ) {
			data.left = null;
		}

		return data;
	},

	_getPaddingPlusBorderDimensions: function( element ) {
		var i = 0,
			widths = [],
			borders = [
				element.css( "borderTopWidth" ),
				element.css( "borderRightWidth" ),
				element.css( "borderBottomWidth" ),
				element.css( "borderLeftWidth" )
			],
			paddings = [
				element.css( "paddingTop" ),
				element.css( "paddingRight" ),
				element.css( "paddingBottom" ),
				element.css( "paddingLeft" )
			];

		for ( ; i < 4; i++ ) {
			widths[ i ] = ( parseFloat( borders[ i ] ) || 0 );
			widths[ i ] += ( parseFloat( paddings[ i ] ) || 0 );
		}

		return {
			height: widths[ 0 ] + widths[ 2 ],
			width: widths[ 1 ] + widths[ 3 ]
		};
	},

	_proportionallyResize: function() {

		if ( !this._proportionallyResizeElements.length ) {
			return;
		}

		var prel,
			i = 0,
			element = this.helper || this.element;

		for ( ; i < this._proportionallyResizeElements.length; i++ ) {

			prel = this._proportionallyResizeElements[ i ];

			// TODO: Seems like a bug to cache this.outerDimensions
			// considering that we are in a loop.
			if ( !this.outerDimensions ) {
				this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
			}

			prel.css( {
				height: ( element.height() - this.outerDimensions.height ) || 0,
				width: ( element.width() - this.outerDimensions.width ) || 0
			} );

		}

	},

	_renderProxy: function() {

		var el = this.element, o = this.options;
		this.elementOffset = el.offset();

		if ( this._helper ) {

			this.helper = this.helper || $( "<div></div>" ).css( { overflow: "hidden" } );

			this._addClass( this.helper, this._helper );
			this.helper.css( {
				width: this.element.outerWidth(),
				height: this.element.outerHeight(),
				position: "absolute",
				left: this.elementOffset.left + "px",
				top: this.elementOffset.top + "px",
				zIndex: ++o.zIndex //TODO: Don't modify option
			} );

			this.helper
				.appendTo( "body" )
				.disableSelection();

		} else {
			this.helper = this.element;
		}

	},

	_change: {
		e: function( event, dx ) {
			return { width: this.originalSize.width + dx };
		},
		w: function( event, dx ) {
			var cs = this.originalSize, sp = this.originalPosition;
			return { left: sp.left + dx, width: cs.width - dx };
		},
		n: function( event, dx, dy ) {
			var cs = this.originalSize, sp = this.originalPosition;
			return { top: sp.top + dy, height: cs.height - dy };
		},
		s: function( event, dx, dy ) {
			return { height: this.originalSize.height + dy };
		},
		se: function( event, dx, dy ) {
			return $.extend( this._change.s.apply( this, arguments ),
				this._change.e.apply( this, [ event, dx, dy ] ) );
		},
		sw: function( event, dx, dy ) {
			return $.extend( this._change.s.apply( this, arguments ),
				this._change.w.apply( this, [ event, dx, dy ] ) );
		},
		ne: function( event, dx, dy ) {
			return $.extend( this._change.n.apply( this, arguments ),
				this._change.e.apply( this, [ event, dx, dy ] ) );
		},
		nw: function( event, dx, dy ) {
			return $.extend( this._change.n.apply( this, arguments ),
				this._change.w.apply( this, [ event, dx, dy ] ) );
		}
	},

	_propagate: function( n, event ) {
		$.ui.plugin.call( this, n, [ event, this.ui() ] );
		if ( n !== "resize" ) {
			this._trigger( n, event, this.ui() );
		}
	},

	plugins: {},

	ui: function() {
		return {
			originalElement: this.originalElement,
			element: this.element,
			helper: this.helper,
			position: this.position,
			size: this.size,
			originalSize: this.originalSize,
			originalPosition: this.originalPosition
		};
	}

} );

/*
 * Resizable Extensions
 */

$.ui.plugin.add( "resizable", "animate", {

	stop: function( event ) {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			pr = that._proportionallyResizeElements,
			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ),
			soffseth = ista && that._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height,
			soffsetw = ista ? 0 : that.sizeDiff.width,
			style = {
				width: ( that.size.width - soffsetw ),
				height: ( that.size.height - soffseth )
			},
			left = ( parseFloat( that.element.css( "left" ) ) +
				( that.position.left - that.originalPosition.left ) ) || null,
			top = ( parseFloat( that.element.css( "top" ) ) +
				( that.position.top - that.originalPosition.top ) ) || null;

		that.element.animate(
			$.extend( style, top && left ? { top: top, left: left } : {} ), {
				duration: o.animateDuration,
				easing: o.animateEasing,
				step: function() {

					var data = {
						width: parseFloat( that.element.css( "width" ) ),
						height: parseFloat( that.element.css( "height" ) ),
						top: parseFloat( that.element.css( "top" ) ),
						left: parseFloat( that.element.css( "left" ) )
					};

					if ( pr && pr.length ) {
						$( pr[ 0 ] ).css( { width: data.width, height: data.height } );
					}

					// Propagating resize, and updating values for each animation step
					that._updateCache( data );
					that._propagate( "resize", event );

				}
			}
		);
	}

} );

$.ui.plugin.add( "resizable", "containment", {

	start: function() {
		var element, p, co, ch, cw, width, height,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			el = that.element,
			oc = o.containment,
			ce = ( oc instanceof $ ) ?
				oc.get( 0 ) :
				( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;

		if ( !ce ) {
			return;
		}

		that.containerElement = $( ce );

		if ( /document/.test( oc ) || oc === document ) {
			that.containerOffset = {
				left: 0,
				top: 0
			};
			that.containerPosition = {
				left: 0,
				top: 0
			};

			that.parentData = {
				element: $( document ),
				left: 0,
				top: 0,
				width: $( document ).width(),
				height: $( document ).height() || document.body.parentNode.scrollHeight
			};
		} else {
			element = $( ce );
			p = [];
			$( [ "Top", "Right", "Left", "Bottom" ] ).each( function( i, name ) {
				p[ i ] = that._num( element.css( "padding" + name ) );
			} );

			that.containerOffset = element.offset();
			that.containerPosition = element.position();
			that.containerSize = {
				height: ( element.innerHeight() - p[ 3 ] ),
				width: ( element.innerWidth() - p[ 1 ] )
			};

			co = that.containerOffset;
			ch = that.containerSize.height;
			cw = that.containerSize.width;
			width = ( that._hasScroll( ce, "left" ) ? ce.scrollWidth : cw );
			height = ( that._hasScroll( ce ) ? ce.scrollHeight : ch );

			that.parentData = {
				element: ce,
				left: co.left,
				top: co.top,
				width: width,
				height: height
			};
		}
	},

	resize: function( event ) {
		var woset, hoset, isParent, isOffsetRelative,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			co = that.containerOffset,
			cp = that.position,
			pRatio = that._aspectRatio || event.shiftKey,
			cop = {
				top: 0,
				left: 0
			},
			ce = that.containerElement,
			continueResize = true;

		if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
			cop = co;
		}

		if ( cp.left < ( that._helper ? co.left : 0 ) ) {
			that.size.width = that.size.width +
				( that._helper ?
					( that.position.left - co.left ) :
					( that.position.left - cop.left ) );

			if ( pRatio ) {
				that.size.height = that.size.width / that.aspectRatio;
				continueResize = false;
			}
			that.position.left = o.helper ? co.left : 0;
		}

		if ( cp.top < ( that._helper ? co.top : 0 ) ) {
			that.size.height = that.size.height +
				( that._helper ?
					( that.position.top - co.top ) :
					that.position.top );

			if ( pRatio ) {
				that.size.width = that.size.height * that.aspectRatio;
				continueResize = false;
			}
			that.position.top = that._helper ? co.top : 0;
		}

		isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
		isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );

		if ( isParent && isOffsetRelative ) {
			that.offset.left = that.parentData.left + that.position.left;
			that.offset.top = that.parentData.top + that.position.top;
		} else {
			that.offset.left = that.element.offset().left;
			that.offset.top = that.element.offset().top;
		}

		woset = Math.abs( that.sizeDiff.width +
			( that._helper ?
				that.offset.left - cop.left :
				( that.offset.left - co.left ) ) );

		hoset = Math.abs( that.sizeDiff.height +
			( that._helper ?
				that.offset.top - cop.top :
				( that.offset.top - co.top ) ) );

		if ( woset + that.size.width >= that.parentData.width ) {
			that.size.width = that.parentData.width - woset;
			if ( pRatio ) {
				that.size.height = that.size.width / that.aspectRatio;
				continueResize = false;
			}
		}

		if ( hoset + that.size.height >= that.parentData.height ) {
			that.size.height = that.parentData.height - hoset;
			if ( pRatio ) {
				that.size.width = that.size.height * that.aspectRatio;
				continueResize = false;
			}
		}

		if ( !continueResize ) {
			that.position.left = that.prevPosition.left;
			that.position.top = that.prevPosition.top;
			that.size.width = that.prevSize.width;
			that.size.height = that.prevSize.height;
		}
	},

	stop: function() {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			co = that.containerOffset,
			cop = that.containerPosition,
			ce = that.containerElement,
			helper = $( that.helper ),
			ho = helper.offset(),
			w = helper.outerWidth() - that.sizeDiff.width,
			h = helper.outerHeight() - that.sizeDiff.height;

		if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
			$( this ).css( {
				left: ho.left - cop.left - co.left,
				width: w,
				height: h
			} );
		}

		if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
			$( this ).css( {
				left: ho.left - cop.left - co.left,
				width: w,
				height: h
			} );
		}
	}
} );

$.ui.plugin.add( "resizable", "alsoResize", {

	start: function() {
		var that = $( this ).resizable( "instance" ),
			o = that.options;

		$( o.alsoResize ).each( function() {
			var el = $( this );
			el.data( "ui-resizable-alsoresize", {
				width: parseFloat( el.width() ), height: parseFloat( el.height() ),
				left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
			} );
		} );
	},

	resize: function( event, ui ) {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			os = that.originalSize,
			op = that.originalPosition,
			delta = {
				height: ( that.size.height - os.height ) || 0,
				width: ( that.size.width - os.width ) || 0,
				top: ( that.position.top - op.top ) || 0,
				left: ( that.position.left - op.left ) || 0
			};

			$( o.alsoResize ).each( function() {
				var el = $( this ), start = $( this ).data( "ui-resizable-alsoresize" ), style = {},
					css = el.parents( ui.originalElement[ 0 ] ).length ?
							[ "width", "height" ] :
							[ "width", "height", "top", "left" ];

				$.each( css, function( i, prop ) {
					var sum = ( start[ prop ] || 0 ) + ( delta[ prop ] || 0 );
					if ( sum && sum >= 0 ) {
						style[ prop ] = sum || null;
					}
				} );

				el.css( style );
			} );
	},

	stop: function() {
		$( this ).removeData( "ui-resizable-alsoresize" );
	}
} );

$.ui.plugin.add( "resizable", "ghost", {

	start: function() {

		var that = $( this ).resizable( "instance" ), cs = that.size;

		that.ghost = that.originalElement.clone();
		that.ghost.css( {
			opacity: 0.25,
			display: "block",
			position: "relative",
			height: cs.height,
			width: cs.width,
			margin: 0,
			left: 0,
			top: 0
		} );

		that._addClass( that.ghost, "ui-resizable-ghost" );

		// DEPRECATED
		// TODO: remove after 1.12
		if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) {

			// Ghost option
			that.ghost.addClass( this.options.ghost );
		}

		that.ghost.appendTo( that.helper );

	},

	resize: function() {
		var that = $( this ).resizable( "instance" );
		if ( that.ghost ) {
			that.ghost.css( {
				position: "relative",
				height: that.size.height,
				width: that.size.width
			} );
		}
	},

	stop: function() {
		var that = $( this ).resizable( "instance" );
		if ( that.ghost && that.helper ) {
			that.helper.get( 0 ).removeChild( that.ghost.get( 0 ) );
		}
	}

} );

$.ui.plugin.add( "resizable", "grid", {

	resize: function() {
		var outerDimensions,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			cs = that.size,
			os = that.originalSize,
			op = that.originalPosition,
			a = that.axis,
			grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
			gridX = ( grid[ 0 ] || 1 ),
			gridY = ( grid[ 1 ] || 1 ),
			ox = Math.round( ( cs.width - os.width ) / gridX ) * gridX,
			oy = Math.round( ( cs.height - os.height ) / gridY ) * gridY,
			newWidth = os.width + ox,
			newHeight = os.height + oy,
			isMaxWidth = o.maxWidth && ( o.maxWidth < newWidth ),
			isMaxHeight = o.maxHeight && ( o.maxHeight < newHeight ),
			isMinWidth = o.minWidth && ( o.minWidth > newWidth ),
			isMinHeight = o.minHeight && ( o.minHeight > newHeight );

		o.grid = grid;

		if ( isMinWidth ) {
			newWidth += gridX;
		}
		if ( isMinHeight ) {
			newHeight += gridY;
		}
		if ( isMaxWidth ) {
			newWidth -= gridX;
		}
		if ( isMaxHeight ) {
			newHeight -= gridY;
		}

		if ( /^(se|s|e)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
		} else if ( /^(ne)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
			that.position.top = op.top - oy;
		} else if ( /^(sw)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
			that.position.left = op.left - ox;
		} else {
			if ( newHeight - gridY <= 0 || newWidth - gridX <= 0 ) {
				outerDimensions = that._getPaddingPlusBorderDimensions( this );
			}

			if ( newHeight - gridY > 0 ) {
				that.size.height = newHeight;
				that.position.top = op.top - oy;
			} else {
				newHeight = gridY - outerDimensions.height;
				that.size.height = newHeight;
				that.position.top = op.top + os.height - newHeight;
			}
			if ( newWidth - gridX > 0 ) {
				that.size.width = newWidth;
				that.position.left = op.left - ox;
			} else {
				newWidth = gridX - outerDimensions.width;
				that.size.width = newWidth;
				that.position.left = op.left + os.width - newWidth;
			}
		}
	}

} );

var widgetsResizable = $.ui.resizable;


/*!
 * jQuery UI Accordion 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Accordion
//>>group: Widgets
/* eslint-disable max-len */
//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
/* eslint-enable max-len */
//>>docs: http://api.jqueryui.com/accordion/
//>>demos: http://jqueryui.com/accordion/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/accordion.css
//>>css.theme: ../../themes/base/theme.css


var widgetsAccordion = $.widget( "ui.accordion", {
	version: "1.13.2",
	options: {
		active: 0,
		animate: {},
		classes: {
			"ui-accordion-header": "ui-corner-top",
			"ui-accordion-header-collapsed": "ui-corner-all",
			"ui-accordion-content": "ui-corner-bottom"
		},
		collapsible: false,
		event: "click",
		header: function( elem ) {
			return elem.find( "> li > :first-child" ).add( elem.find( "> :not(li)" ).even() );
		},
		heightStyle: "auto",
		icons: {
			activeHeader: "ui-icon-triangle-1-s",
			header: "ui-icon-triangle-1-e"
		},

		// Callbacks
		activate: null,
		beforeActivate: null
	},

	hideProps: {
		borderTopWidth: "hide",
		borderBottomWidth: "hide",
		paddingTop: "hide",
		paddingBottom: "hide",
		height: "hide"
	},

	showProps: {
		borderTopWidth: "show",
		borderBottomWidth: "show",
		paddingTop: "show",
		paddingBottom: "show",
		height: "show"
	},

	_create: function() {
		var options = this.options;

		this.prevShow = this.prevHide = $();
		this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
		this.element.attr( "role", "tablist" );

		// Don't allow collapsible: false and active: false / null
		if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
			options.active = 0;
		}

		this._processPanels();

		// handle negative values
		if ( options.active < 0 ) {
			options.active += this.headers.length;
		}
		this._refresh();
	},

	_getCreateEventData: function() {
		return {
			header: this.active,
			panel: !this.active.length ? $() : this.active.next()
		};
	},

	_createIcons: function() {
		var icon, children,
			icons = this.options.icons;

		if ( icons ) {
			icon = $( "<span>" );
			this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
			icon.prependTo( this.headers );
			children = this.active.children( ".ui-accordion-header-icon" );
			this._removeClass( children, icons.header )
				._addClass( children, null, icons.activeHeader )
				._addClass( this.headers, "ui-accordion-icons" );
		}
	},

	_destroyIcons: function() {
		this._removeClass( this.headers, "ui-accordion-icons" );
		this.headers.children( ".ui-accordion-header-icon" ).remove();
	},

	_destroy: function() {
		var contents;

		// Clean up main element
		this.element.removeAttr( "role" );

		// Clean up headers
		this.headers
			.removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
			.removeUniqueId();

		this._destroyIcons();

		// Clean up content panels
		contents = this.headers.next()
			.css( "display", "" )
			.removeAttr( "role aria-hidden aria-labelledby" )
			.removeUniqueId();

		if ( this.options.heightStyle !== "content" ) {
			contents.css( "height", "" );
		}
	},

	_setOption: function( key, value ) {
		if ( key === "active" ) {

			// _activate() will handle invalid values and update this.options
			this._activate( value );
			return;
		}

		if ( key === "event" ) {
			if ( this.options.event ) {
				this._off( this.headers, this.options.event );
			}
			this._setupEvents( value );
		}

		this._super( key, value );

		// Setting collapsible: false while collapsed; open first panel
		if ( key === "collapsible" && !value && this.options.active === false ) {
			this._activate( 0 );
		}

		if ( key === "icons" ) {
			this._destroyIcons();
			if ( value ) {
				this._createIcons();
			}
		}
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this.element.attr( "aria-disabled", value );

		// Support: IE8 Only
		// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
		// so we need to add the disabled class to the headers and panels
		this._toggleClass( null, "ui-state-disabled", !!value );
		this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
			!!value );
	},

	_keydown: function( event ) {
		if ( event.altKey || event.ctrlKey ) {
			return;
		}

		var keyCode = $.ui.keyCode,
			length = this.headers.length,
			currentIndex = this.headers.index( event.target ),
			toFocus = false;

		switch ( event.keyCode ) {
		case keyCode.RIGHT:
		case keyCode.DOWN:
			toFocus = this.headers[ ( currentIndex + 1 ) % length ];
			break;
		case keyCode.LEFT:
		case keyCode.UP:
			toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
			break;
		case keyCode.SPACE:
		case keyCode.ENTER:
			this._eventHandler( event );
			break;
		case keyCode.HOME:
			toFocus = this.headers[ 0 ];
			break;
		case keyCode.END:
			toFocus = this.headers[ length - 1 ];
			break;
		}

		if ( toFocus ) {
			$( event.target ).attr( "tabIndex", -1 );
			$( toFocus ).attr( "tabIndex", 0 );
			$( toFocus ).trigger( "focus" );
			event.preventDefault();
		}
	},

	_panelKeyDown: function( event ) {
		if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
			$( event.currentTarget ).prev().trigger( "focus" );
		}
	},

	refresh: function() {
		var options = this.options;
		this._processPanels();

		// Was collapsed or no panel
		if ( ( options.active === false && options.collapsible === true ) ||
				!this.headers.length ) {
			options.active = false;
			this.active = $();

		// active false only when collapsible is true
		} else if ( options.active === false ) {
			this._activate( 0 );

		// was active, but active panel is gone
		} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {

			// all remaining panel are disabled
			if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
				options.active = false;
				this.active = $();

			// activate previous panel
			} else {
				this._activate( Math.max( 0, options.active - 1 ) );
			}

		// was active, active panel still exists
		} else {

			// make sure active index is correct
			options.active = this.headers.index( this.active );
		}

		this._destroyIcons();

		this._refresh();
	},

	_processPanels: function() {
		var prevHeaders = this.headers,
			prevPanels = this.panels;

		if ( typeof this.options.header === "function" ) {
			this.headers = this.options.header( this.element );
		} else {
			this.headers = this.element.find( this.options.header );
		}
		this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
			"ui-state-default" );

		this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
		this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );

		// Avoid memory leaks (#10056)
		if ( prevPanels ) {
			this._off( prevHeaders.not( this.headers ) );
			this._off( prevPanels.not( this.panels ) );
		}
	},

	_refresh: function() {
		var maxHeight,
			options = this.options,
			heightStyle = options.heightStyle,
			parent = this.element.parent();

		this.active = this._findActive( options.active );
		this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
			._removeClass( this.active, "ui-accordion-header-collapsed" );
		this._addClass( this.active.next(), "ui-accordion-content-active" );
		this.active.next().show();

		this.headers
			.attr( "role", "tab" )
			.each( function() {
				var header = $( this ),
					headerId = header.uniqueId().attr( "id" ),
					panel = header.next(),
					panelId = panel.uniqueId().attr( "id" );
				header.attr( "aria-controls", panelId );
				panel.attr( "aria-labelledby", headerId );
			} )
			.next()
				.attr( "role", "tabpanel" );

		this.headers
			.not( this.active )
				.attr( {
					"aria-selected": "false",
					"aria-expanded": "false",
					tabIndex: -1
				} )
				.next()
					.attr( {
						"aria-hidden": "true"
					} )
					.hide();

		// Make sure at least one header is in the tab order
		if ( !this.active.length ) {
			this.headers.eq( 0 ).attr( "tabIndex", 0 );
		} else {
			this.active.attr( {
				"aria-selected": "true",
				"aria-expanded": "true",
				tabIndex: 0
			} )
				.next()
					.attr( {
						"aria-hidden": "false"
					} );
		}

		this._createIcons();

		this._setupEvents( options.event );

		if ( heightStyle === "fill" ) {
			maxHeight = parent.height();
			this.element.siblings( ":visible" ).each( function() {
				var elem = $( this ),
					position = elem.css( "position" );

				if ( position === "absolute" || position === "fixed" ) {
					return;
				}
				maxHeight -= elem.outerHeight( true );
			} );

			this.headers.each( function() {
				maxHeight -= $( this ).outerHeight( true );
			} );

			this.headers.next()
				.each( function() {
					$( this ).height( Math.max( 0, maxHeight -
						$( this ).innerHeight() + $( this ).height() ) );
				} )
				.css( "overflow", "auto" );
		} else if ( heightStyle === "auto" ) {
			maxHeight = 0;
			this.headers.next()
				.each( function() {
					var isVisible = $( this ).is( ":visible" );
					if ( !isVisible ) {
						$( this ).show();
					}
					maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
					if ( !isVisible ) {
						$( this ).hide();
					}
				} )
				.height( maxHeight );
		}
	},

	_activate: function( index ) {
		var active = this._findActive( index )[ 0 ];

		// Trying to activate the already active panel
		if ( active === this.active[ 0 ] ) {
			return;
		}

		// Trying to collapse, simulate a click on the currently active header
		active = active || this.active[ 0 ];

		this._eventHandler( {
			target: active,
			currentTarget: active,
			preventDefault: $.noop
		} );
	},

	_findActive: function( selector ) {
		return typeof selector === "number" ? this.headers.eq( selector ) : $();
	},

	_setupEvents: function( event ) {
		var events = {
			keydown: "_keydown"
		};
		if ( event ) {
			$.each( event.split( " " ), function( index, eventName ) {
				events[ eventName ] = "_eventHandler";
			} );
		}

		this._off( this.headers.add( this.headers.next() ) );
		this._on( this.headers, events );
		this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
		this._hoverable( this.headers );
		this._focusable( this.headers );
	},

	_eventHandler: function( event ) {
		var activeChildren, clickedChildren,
			options = this.options,
			active = this.active,
			clicked = $( event.currentTarget ),
			clickedIsActive = clicked[ 0 ] === active[ 0 ],
			collapsing = clickedIsActive && options.collapsible,
			toShow = collapsing ? $() : clicked.next(),
			toHide = active.next(),
			eventData = {
				oldHeader: active,
				oldPanel: toHide,
				newHeader: collapsing ? $() : clicked,
				newPanel: toShow
			};

		event.preventDefault();

		if (

				// click on active header, but not collapsible
				( clickedIsActive && !options.collapsible ) ||

				// allow canceling activation
				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
			return;
		}

		options.active = collapsing ? false : this.headers.index( clicked );

		// When the call to ._toggle() comes after the class changes
		// it causes a very odd bug in IE 8 (see #6720)
		this.active = clickedIsActive ? $() : clicked;
		this._toggle( eventData );

		// Switch classes
		// corner classes on the previously active header stay after the animation
		this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
		if ( options.icons ) {
			activeChildren = active.children( ".ui-accordion-header-icon" );
			this._removeClass( activeChildren, null, options.icons.activeHeader )
				._addClass( activeChildren, null, options.icons.header );
		}

		if ( !clickedIsActive ) {
			this._removeClass( clicked, "ui-accordion-header-collapsed" )
				._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
			if ( options.icons ) {
				clickedChildren = clicked.children( ".ui-accordion-header-icon" );
				this._removeClass( clickedChildren, null, options.icons.header )
					._addClass( clickedChildren, null, options.icons.activeHeader );
			}

			this._addClass( clicked.next(), "ui-accordion-content-active" );
		}
	},

	_toggle: function( data ) {
		var toShow = data.newPanel,
			toHide = this.prevShow.length ? this.prevShow : data.oldPanel;

		// Handle activating a panel during the animation for another activation
		this.prevShow.add( this.prevHide ).stop( true, true );
		this.prevShow = toShow;
		this.prevHide = toHide;

		if ( this.options.animate ) {
			this._animate( toShow, toHide, data );
		} else {
			toHide.hide();
			toShow.show();
			this._toggleComplete( data );
		}

		toHide.attr( {
			"aria-hidden": "true"
		} );
		toHide.prev().attr( {
			"aria-selected": "false",
			"aria-expanded": "false"
		} );

		// if we're switching panels, remove the old header from the tab order
		// if we're opening from collapsed state, remove the previous header from the tab order
		// if we're collapsing, then keep the collapsing header in the tab order
		if ( toShow.length && toHide.length ) {
			toHide.prev().attr( {
				"tabIndex": -1,
				"aria-expanded": "false"
			} );
		} else if ( toShow.length ) {
			this.headers.filter( function() {
				return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
			} )
				.attr( "tabIndex", -1 );
		}

		toShow
			.attr( "aria-hidden", "false" )
			.prev()
				.attr( {
					"aria-selected": "true",
					"aria-expanded": "true",
					tabIndex: 0
				} );
	},

	_animate: function( toShow, toHide, data ) {
		var total, easing, duration,
			that = this,
			adjust = 0,
			boxSizing = toShow.css( "box-sizing" ),
			down = toShow.length &&
				( !toHide.length || ( toShow.index() < toHide.index() ) ),
			animate = this.options.animate || {},
			options = down && animate.down || animate,
			complete = function() {
				that._toggleComplete( data );
			};

		if ( typeof options === "number" ) {
			duration = options;
		}
		if ( typeof options === "string" ) {
			easing = options;
		}

		// fall back from options to animation in case of partial down settings
		easing = easing || options.easing || animate.easing;
		duration = duration || options.duration || animate.duration;

		if ( !toHide.length ) {
			return toShow.animate( this.showProps, duration, easing, complete );
		}
		if ( !toShow.length ) {
			return toHide.animate( this.hideProps, duration, easing, complete );
		}

		total = toShow.show().outerHeight();
		toHide.animate( this.hideProps, {
			duration: duration,
			easing: easing,
			step: function( now, fx ) {
				fx.now = Math.round( now );
			}
		} );
		toShow
			.hide()
			.animate( this.showProps, {
				duration: duration,
				easing: easing,
				complete: complete,
				step: function( now, fx ) {
					fx.now = Math.round( now );
					if ( fx.prop !== "height" ) {
						if ( boxSizing === "content-box" ) {
							adjust += fx.now;
						}
					} else if ( that.options.heightStyle !== "content" ) {
						fx.now = Math.round( total - toHide.outerHeight() - adjust );
						adjust = 0;
					}
				}
			} );
	},

	_toggleComplete: function( data ) {
		var toHide = data.oldPanel,
			prev = toHide.prev();

		this._removeClass( toHide, "ui-accordion-content-active" );
		this._removeClass( prev, "ui-accordion-header-active" )
			._addClass( prev, "ui-accordion-header-collapsed" );

		// Work around for rendering bug in IE (#5421)
		if ( toHide.length ) {
			toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
		}
		this._trigger( "activate", null, data );
	}
} );


/*!
 * jQuery UI Controlgroup 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Controlgroup
//>>group: Widgets
//>>description: Visually groups form control widgets
//>>docs: http://api.jqueryui.com/controlgroup/
//>>demos: http://jqueryui.com/controlgroup/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/controlgroup.css
//>>css.theme: ../../themes/base/theme.css


var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g;

var widgetsControlgroup = $.widget( "ui.controlgroup", {
	version: "1.13.2",
	defaultElement: "<div>",
	options: {
		direction: "horizontal",
		disabled: null,
		onlyVisible: true,
		items: {
			"button": "input[type=button], input[type=submit], input[type=reset], button, a",
			"controlgroupLabel": ".ui-controlgroup-label",
			"checkboxradio": "input[type='checkbox'], input[type='radio']",
			"selectmenu": "select",
			"spinner": ".ui-spinner-input"
		}
	},

	_create: function() {
		this._enhance();
	},

	// To support the enhanced option in jQuery Mobile, we isolate DOM manipulation
	_enhance: function() {
		this.element.attr( "role", "toolbar" );
		this.refresh();
	},

	_destroy: function() {
		this._callChildMethod( "destroy" );
		this.childWidgets.removeData( "ui-controlgroup-data" );
		this.element.removeAttr( "role" );
		if ( this.options.items.controlgroupLabel ) {
			this.element
				.find( this.options.items.controlgroupLabel )
				.find( ".ui-controlgroup-label-contents" )
				.contents().unwrap();
		}
	},

	_initWidgets: function() {
		var that = this,
			childWidgets = [];

		// First we iterate over each of the items options
		$.each( this.options.items, function( widget, selector ) {
			var labels;
			var options = {};

			// Make sure the widget has a selector set
			if ( !selector ) {
				return;
			}

			if ( widget === "controlgroupLabel" ) {
				labels = that.element.find( selector );
				labels.each( function() {
					var element = $( this );

					if ( element.children( ".ui-controlgroup-label-contents" ).length ) {
						return;
					}
					element.contents()
						.wrapAll( "<span class='ui-controlgroup-label-contents'></span>" );
				} );
				that._addClass( labels, null, "ui-widget ui-widget-content ui-state-default" );
				childWidgets = childWidgets.concat( labels.get() );
				return;
			}

			// Make sure the widget actually exists
			if ( !$.fn[ widget ] ) {
				return;
			}

			// We assume everything is in the middle to start because we can't determine
			// first / last elements until all enhancments are done.
			if ( that[ "_" + widget + "Options" ] ) {
				options = that[ "_" + widget + "Options" ]( "middle" );
			} else {
				options = { classes: {} };
			}

			// Find instances of this widget inside controlgroup and init them
			that.element
				.find( selector )
				.each( function() {
					var element = $( this );
					var instance = element[ widget ]( "instance" );

					// We need to clone the default options for this type of widget to avoid
					// polluting the variable options which has a wider scope than a single widget.
					var instanceOptions = $.widget.extend( {}, options );

					// If the button is the child of a spinner ignore it
					// TODO: Find a more generic solution
					if ( widget === "button" && element.parent( ".ui-spinner" ).length ) {
						return;
					}

					// Create the widget if it doesn't exist
					if ( !instance ) {
						instance = element[ widget ]()[ widget ]( "instance" );
					}
					if ( instance ) {
						instanceOptions.classes =
							that._resolveClassesValues( instanceOptions.classes, instance );
					}
					element[ widget ]( instanceOptions );

					// Store an instance of the controlgroup to be able to reference
					// from the outermost element for changing options and refresh
					var widgetElement = element[ widget ]( "widget" );
					$.data( widgetElement[ 0 ], "ui-controlgroup-data",
						instance ? instance : element[ widget ]( "instance" ) );

					childWidgets.push( widgetElement[ 0 ] );
				} );
		} );

		this.childWidgets = $( $.uniqueSort( childWidgets ) );
		this._addClass( this.childWidgets, "ui-controlgroup-item" );
	},

	_callChildMethod: function( method ) {
		this.childWidgets.each( function() {
			var element = $( this ),
				data = element.data( "ui-controlgroup-data" );
			if ( data && data[ method ] ) {
				data[ method ]();
			}
		} );
	},

	_updateCornerClass: function( element, position ) {
		var remove = "ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all";
		var add = this._buildSimpleOptions( position, "label" ).classes.label;

		this._removeClass( element, null, remove );
		this._addClass( element, null, add );
	},

	_buildSimpleOptions: function( position, key ) {
		var direction = this.options.direction === "vertical";
		var result = {
			classes: {}
		};
		result.classes[ key ] = {
			"middle": "",
			"first": "ui-corner-" + ( direction ? "top" : "left" ),
			"last": "ui-corner-" + ( direction ? "bottom" : "right" ),
			"only": "ui-corner-all"
		}[ position ];

		return result;
	},

	_spinnerOptions: function( position ) {
		var options = this._buildSimpleOptions( position, "ui-spinner" );

		options.classes[ "ui-spinner-up" ] = "";
		options.classes[ "ui-spinner-down" ] = "";

		return options;
	},

	_buttonOptions: function( position ) {
		return this._buildSimpleOptions( position, "ui-button" );
	},

	_checkboxradioOptions: function( position ) {
		return this._buildSimpleOptions( position, "ui-checkboxradio-label" );
	},

	_selectmenuOptions: function( position ) {
		var direction = this.options.direction === "vertical";
		return {
			width: direction ? "auto" : false,
			classes: {
				middle: {
					"ui-selectmenu-button-open": "",
					"ui-selectmenu-button-closed": ""
				},
				first: {
					"ui-selectmenu-button-open": "ui-corner-" + ( direction ? "top" : "tl" ),
					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "top" : "left" )
				},
				last: {
					"ui-selectmenu-button-open": direction ? "" : "ui-corner-tr",
					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "bottom" : "right" )
				},
				only: {
					"ui-selectmenu-button-open": "ui-corner-top",
					"ui-selectmenu-button-closed": "ui-corner-all"
				}

			}[ position ]
		};
	},

	_resolveClassesValues: function( classes, instance ) {
		var result = {};
		$.each( classes, function( key ) {
			var current = instance.options.classes[ key ] || "";
			current = String.prototype.trim.call( current.replace( controlgroupCornerRegex, "" ) );
			result[ key ] = ( current + " " + classes[ key ] ).replace( /\s+/g, " " );
		} );
		return result;
	},

	_setOption: function( key, value ) {
		if ( key === "direction" ) {
			this._removeClass( "ui-controlgroup-" + this.options.direction );
		}

		this._super( key, value );
		if ( key === "disabled" ) {
			this._callChildMethod( value ? "disable" : "enable" );
			return;
		}

		this.refresh();
	},

	refresh: function() {
		var children,
			that = this;

		this._addClass( "ui-controlgroup ui-controlgroup-" + this.options.direction );

		if ( this.options.direction === "horizontal" ) {
			this._addClass( null, "ui-helper-clearfix" );
		}
		this._initWidgets();

		children = this.childWidgets;

		// We filter here because we need to track all childWidgets not just the visible ones
		if ( this.options.onlyVisible ) {
			children = children.filter( ":visible" );
		}

		if ( children.length ) {

			// We do this last because we need to make sure all enhancment is done
			// before determining first and last
			$.each( [ "first", "last" ], function( index, value ) {
				var instance = children[ value ]().data( "ui-controlgroup-data" );

				if ( instance && that[ "_" + instance.widgetName + "Options" ] ) {
					var options = that[ "_" + instance.widgetName + "Options" ](
						children.length === 1 ? "only" : value
					);
					options.classes = that._resolveClassesValues( options.classes, instance );
					instance.element[ instance.widgetName ]( options );
				} else {
					that._updateCornerClass( children[ value ](), value );
				}
			} );

			// Finally call the refresh method on each of the child widgets.
			this._callChildMethod( "refresh" );
		}
	}
} );

/*!
 * jQuery UI Checkboxradio 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Checkboxradio
//>>group: Widgets
//>>description: Enhances a form with multiple themeable checkboxes or radio buttons.
//>>docs: http://api.jqueryui.com/checkboxradio/
//>>demos: http://jqueryui.com/checkboxradio/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/button.css
//>>css.structure: ../../themes/base/checkboxradio.css
//>>css.theme: ../../themes/base/theme.css


$.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
	version: "1.13.2",
	options: {
		disabled: null,
		label: null,
		icon: true,
		classes: {
			"ui-checkboxradio-label": "ui-corner-all",
			"ui-checkboxradio-icon": "ui-corner-all"
		}
	},

	_getCreateOptions: function() {
		var disabled, labels, labelContents;
		var options = this._super() || {};

		// We read the type here, because it makes more sense to throw a element type error first,
		// rather then the error for lack of a label. Often if its the wrong type, it
		// won't have a label (e.g. calling on a div, btn, etc)
		this._readType();

		labels = this.element.labels();

		// If there are multiple labels, use the last one
		this.label = $( labels[ labels.length - 1 ] );
		if ( !this.label.length ) {
			$.error( "No label found for checkboxradio widget" );
		}

		this.originalLabel = "";

		// We need to get the label text but this may also need to make sure it does not contain the
		// input itself.
		// The label contents could be text, html, or a mix. We wrap all elements
		// and read the wrapper's `innerHTML` to get a string representation of
		// the label, without the input as part of it.
		labelContents = this.label.contents().not( this.element[ 0 ] );

		if ( labelContents.length ) {
			this.originalLabel += labelContents
				.clone()
				.wrapAll( "<div></div>" )
				.parent()
				.html();
		}

		// Set the label option if we found label text
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}
		return options;
	},

	_create: function() {
		var checked = this.element[ 0 ].checked;

		this._bindFormResetHandler();

		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled;
		}

		this._setOption( "disabled", this.options.disabled );
		this._addClass( "ui-checkboxradio", "ui-helper-hidden-accessible" );
		this._addClass( this.label, "ui-checkboxradio-label", "ui-button ui-widget" );

		if ( this.type === "radio" ) {
			this._addClass( this.label, "ui-checkboxradio-radio-label" );
		}

		if ( this.options.label && this.options.label !== this.originalLabel ) {
			this._updateLabel();
		} else if ( this.originalLabel ) {
			this.options.label = this.originalLabel;
		}

		this._enhance();

		if ( checked ) {
			this._addClass( this.label, "ui-checkboxradio-checked", "ui-state-active" );
		}

		this._on( {
			change: "_toggleClasses",
			focus: function() {
				this._addClass( this.label, null, "ui-state-focus ui-visual-focus" );
			},
			blur: function() {
				this._removeClass( this.label, null, "ui-state-focus ui-visual-focus" );
			}
		} );
	},

	_readType: function() {
		var nodeName = this.element[ 0 ].nodeName.toLowerCase();
		this.type = this.element[ 0 ].type;
		if ( nodeName !== "input" || !/radio|checkbox/.test( this.type ) ) {
			$.error( "Can't create checkboxradio on element.nodeName=" + nodeName +
				" and element.type=" + this.type );
		}
	},

	// Support jQuery Mobile enhanced option
	_enhance: function() {
		this._updateIcon( this.element[ 0 ].checked );
	},

	widget: function() {
		return this.label;
	},

	_getRadioGroup: function() {
		var group;
		var name = this.element[ 0 ].name;
		var nameSelector = "input[name='" + $.escapeSelector( name ) + "']";

		if ( !name ) {
			return $( [] );
		}

		if ( this.form.length ) {
			group = $( this.form[ 0 ].elements ).filter( nameSelector );
		} else {

			// Not inside a form, check all inputs that also are not inside a form
			group = $( nameSelector ).filter( function() {
				return $( this )._form().length === 0;
			} );
		}

		return group.not( this.element );
	},

	_toggleClasses: function() {
		var checked = this.element[ 0 ].checked;
		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );

		if ( this.options.icon && this.type === "checkbox" ) {
			this._toggleClass( this.icon, null, "ui-icon-check ui-state-checked", checked )
				._toggleClass( this.icon, null, "ui-icon-blank", !checked );
		}

		if ( this.type === "radio" ) {
			this._getRadioGroup()
				.each( function() {
					var instance = $( this ).checkboxradio( "instance" );

					if ( instance ) {
						instance._removeClass( instance.label,
							"ui-checkboxradio-checked", "ui-state-active" );
					}
				} );
		}
	},

	_destroy: function() {
		this._unbindFormResetHandler();

		if ( this.icon ) {
			this.icon.remove();
			this.iconSpace.remove();
		}
	},

	_setOption: function( key, value ) {

		// We don't allow the value to be set to nothing
		if ( key === "label" && !value ) {
			return;
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( this.label, null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;

			// Don't refresh when setting disabled
			return;
		}
		this.refresh();
	},

	_updateIcon: function( checked ) {
		var toAdd = "ui-icon ui-icon-background ";

		if ( this.options.icon ) {
			if ( !this.icon ) {
				this.icon = $( "<span>" );
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-checkboxradio-icon-space" );
			}

			if ( this.type === "checkbox" ) {
				toAdd += checked ? "ui-icon-check ui-state-checked" : "ui-icon-blank";
				this._removeClass( this.icon, null, checked ? "ui-icon-blank" : "ui-icon-check" );
			} else {
				toAdd += "ui-icon-blank";
			}
			this._addClass( this.icon, "ui-checkboxradio-icon", toAdd );
			if ( !checked ) {
				this._removeClass( this.icon, null, "ui-icon-check ui-state-checked" );
			}
			this.icon.prependTo( this.label ).after( this.iconSpace );
		} else if ( this.icon !== undefined ) {
			this.icon.remove();
			this.iconSpace.remove();
			delete this.icon;
		}
	},

	_updateLabel: function() {

		// Remove the contents of the label ( minus the icon, icon space, and input )
		var contents = this.label.contents().not( this.element[ 0 ] );
		if ( this.icon ) {
			contents = contents.not( this.icon[ 0 ] );
		}
		if ( this.iconSpace ) {
			contents = contents.not( this.iconSpace[ 0 ] );
		}
		contents.remove();

		this.label.append( this.options.label );
	},

	refresh: function() {
		var checked = this.element[ 0 ].checked,
			isDisabled = this.element[ 0 ].disabled;

		this._updateIcon( checked );
		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
		if ( this.options.label !== null ) {
			this._updateLabel();
		}

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { "disabled": isDisabled } );
		}
	}

} ] );

var widgetsCheckboxradio = $.ui.checkboxradio;


/*!
 * jQuery UI Button 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Button
//>>group: Widgets
//>>description: Enhances a form with themeable buttons.
//>>docs: http://api.jqueryui.com/button/
//>>demos: http://jqueryui.com/button/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/button.css
//>>css.theme: ../../themes/base/theme.css


$.widget( "ui.button", {
	version: "1.13.2",
	defaultElement: "<button>",
	options: {
		classes: {
			"ui-button": "ui-corner-all"
		},
		disabled: null,
		icon: null,
		iconPosition: "beginning",
		label: null,
		showLabel: true
	},

	_getCreateOptions: function() {
		var disabled,

			// This is to support cases like in jQuery Mobile where the base widget does have
			// an implementation of _getCreateOptions
			options = this._super() || {};

		this.isInput = this.element.is( "input" );

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}

		this.originalLabel = this.isInput ? this.element.val() : this.element.html();
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		return options;
	},

	_create: function() {
		if ( !this.option.showLabel & !this.options.icon ) {
			this.options.showLabel = true;
		}

		// We have to check the option again here even though we did in _getCreateOptions,
		// because null may have been passed on init which would override what was set in
		// _getCreateOptions
		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled || false;
		}

		this.hasTitle = !!this.element.attr( "title" );

		// Check to see if the label needs to be set or if its already correct
		if ( this.options.label && this.options.label !== this.originalLabel ) {
			if ( this.isInput ) {
				this.element.val( this.options.label );
			} else {
				this.element.html( this.options.label );
			}
		}
		this._addClass( "ui-button", "ui-widget" );
		this._setOption( "disabled", this.options.disabled );
		this._enhance();

		if ( this.element.is( "a" ) ) {
			this._on( {
				"keyup": function( event ) {
					if ( event.keyCode === $.ui.keyCode.SPACE ) {
						event.preventDefault();

						// Support: PhantomJS <= 1.9, IE 8 Only
						// If a native click is available use it so we actually cause navigation
						// otherwise just trigger a click event
						if ( this.element[ 0 ].click ) {
							this.element[ 0 ].click();
						} else {
							this.element.trigger( "click" );
						}
					}
				}
			} );
		}
	},

	_enhance: function() {
		if ( !this.element.is( "button" ) ) {
			this.element.attr( "role", "button" );
		}

		if ( this.options.icon ) {
			this._updateIcon( "icon", this.options.icon );
			this._updateTooltip();
		}
	},

	_updateTooltip: function() {
		this.title = this.element.attr( "title" );

		if ( !this.options.showLabel && !this.title ) {
			this.element.attr( "title", this.options.label );
		}
	},

	_updateIcon: function( option, value ) {
		var icon = option !== "iconPosition",
			position = icon ? this.options.iconPosition : value,
			displayBlock = position === "top" || position === "bottom";

		// Create icon
		if ( !this.icon ) {
			this.icon = $( "<span>" );

			this._addClass( this.icon, "ui-button-icon", "ui-icon" );

			if ( !this.options.showLabel ) {
				this._addClass( "ui-button-icon-only" );
			}
		} else if ( icon ) {

			// If we are updating the icon remove the old icon class
			this._removeClass( this.icon, null, this.options.icon );
		}

		// If we are updating the icon add the new icon class
		if ( icon ) {
			this._addClass( this.icon, null, value );
		}

		this._attachIcon( position );

		// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
		// the iconSpace if there is one.
		if ( displayBlock ) {
			this._addClass( this.icon, null, "ui-widget-icon-block" );
			if ( this.iconSpace ) {
				this.iconSpace.remove();
			}
		} else {

			// Position is beginning or end so remove the ui-widget-icon-block class and add the
			// space if it does not exist
			if ( !this.iconSpace ) {
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-button-icon-space" );
			}
			this._removeClass( this.icon, null, "ui-wiget-icon-block" );
			this._attachIconSpace( position );
		}
	},

	_destroy: function() {
		this.element.removeAttr( "role" );

		if ( this.icon ) {
			this.icon.remove();
		}
		if ( this.iconSpace ) {
			this.iconSpace.remove();
		}
		if ( !this.hasTitle ) {
			this.element.removeAttr( "title" );
		}
	},

	_attachIconSpace: function( iconPosition ) {
		this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
	},

	_attachIcon: function( iconPosition ) {
		this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
	},

	_setOptions: function( options ) {
		var newShowLabel = options.showLabel === undefined ?
				this.options.showLabel :
				options.showLabel,
			newIcon = options.icon === undefined ? this.options.icon : options.icon;

		if ( !newShowLabel && !newIcon ) {
			options.showLabel = true;
		}
		this._super( options );
	},

	_setOption: function( key, value ) {
		if ( key === "icon" ) {
			if ( value ) {
				this._updateIcon( key, value );
			} else if ( this.icon ) {
				this.icon.remove();
				if ( this.iconSpace ) {
					this.iconSpace.remove();
				}
			}
		}

		if ( key === "iconPosition" ) {
			this._updateIcon( key, value );
		}

		// Make sure we can't end up with a button that has neither text nor icon
		if ( key === "showLabel" ) {
				this._toggleClass( "ui-button-icon-only", null, !value );
				this._updateTooltip();
		}

		if ( key === "label" ) {
			if ( this.isInput ) {
				this.element.val( value );
			} else {

				// If there is an icon, append it, else nothing then append the value
				// this avoids removal of the icon when setting label text
				this.element.html( value );
				if ( this.icon ) {
					this._attachIcon( this.options.iconPosition );
					this._attachIconSpace( this.options.iconPosition );
				}
			}
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;
			if ( value ) {
				this.element.trigger( "blur" );
			}
		}
	},

	refresh: function() {

		// Make sure to only check disabled if its an element that supports this otherwise
		// check for the disabled class to determine state
		var isDisabled = this.element.is( "input, button" ) ?
			this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { disabled: isDisabled } );
		}

		this._updateTooltip();
	}
} );

// DEPRECATED
if ( $.uiBackCompat !== false ) {

	// Text and Icons options
	$.widget( "ui.button", $.ui.button, {
		options: {
			text: true,
			icons: {
				primary: null,
				secondary: null
			}
		},

		_create: function() {
			if ( this.options.showLabel && !this.options.text ) {
				this.options.showLabel = this.options.text;
			}
			if ( !this.options.showLabel && this.options.text ) {
				this.options.text = this.options.showLabel;
			}
			if ( !this.options.icon && ( this.options.icons.primary ||
					this.options.icons.secondary ) ) {
				if ( this.options.icons.primary ) {
					this.options.icon = this.options.icons.primary;
				} else {
					this.options.icon = this.options.icons.secondary;
					this.options.iconPosition = "end";
				}
			} else if ( this.options.icon ) {
				this.options.icons.primary = this.options.icon;
			}
			this._super();
		},

		_setOption: function( key, value ) {
			if ( key === "text" ) {
				this._super( "showLabel", value );
				return;
			}
			if ( key === "showLabel" ) {
				this.options.text = value;
			}
			if ( key === "icon" ) {
				this.options.icons.primary = value;
			}
			if ( key === "icons" ) {
				if ( value.primary ) {
					this._super( "icon", value.primary );
					this._super( "iconPosition", "beginning" );
				} else if ( value.secondary ) {
					this._super( "icon", value.secondary );
					this._super( "iconPosition", "end" );
				}
			}
			this._superApply( arguments );
		}
	} );

	$.fn.button = ( function( orig ) {
		return function( options ) {
			var isMethodCall = typeof options === "string";
			var args = Array.prototype.slice.call( arguments, 1 );
			var returnValue = this;

			if ( isMethodCall ) {

				// If this is an empty collection, we need to have the instance method
				// return undefined instead of the jQuery instance
				if ( !this.length && options === "instance" ) {
					returnValue = undefined;
				} else {
					this.each( function() {
						var methodValue;
						var type = $( this ).attr( "type" );
						var name = type !== "checkbox" && type !== "radio" ?
							"button" :
							"checkboxradio";
						var instance = $.data( this, "ui-" + name );

						if ( options === "instance" ) {
							returnValue = instance;
							return false;
						}

						if ( !instance ) {
							return $.error( "cannot call methods on button" +
								" prior to initialization; " +
								"attempted to call method '" + options + "'" );
						}

						if ( typeof instance[ options ] !== "function" ||
							options.charAt( 0 ) === "_" ) {
							return $.error( "no such method '" + options + "' for button" +
								" widget instance" );
						}

						methodValue = instance[ options ].apply( instance, args );

						if ( methodValue !== instance && methodValue !== undefined ) {
							returnValue = methodValue && methodValue.jquery ?
								returnValue.pushStack( methodValue.get() ) :
								methodValue;
							return false;
						}
					} );
				}
			} else {

				// Allow multiple hashes to be passed on init
				if ( args.length ) {
					options = $.widget.extend.apply( null, [ options ].concat( args ) );
				}

				this.each( function() {
					var type = $( this ).attr( "type" );
					var name = type !== "checkbox" && type !== "radio" ? "button" : "checkboxradio";
					var instance = $.data( this, "ui-" + name );

					if ( instance ) {
						instance.option( options || {} );
						if ( instance._init ) {
							instance._init();
						}
					} else {
						if ( name === "button" ) {
							orig.call( $( this ), options );
							return;
						}

						$( this ).checkboxradio( $.extend( { icon: false }, options ) );
					}
				} );
			}

			return returnValue;
		};
	} )( $.fn.button );

	$.fn.buttonset = function() {
		if ( !$.ui.controlgroup ) {
			$.error( "Controlgroup widget missing" );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
			return this.controlgroup.apply( this,
				[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
			return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
		}
		if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
			arguments[ 0 ].items = {
				button: arguments[ 0 ].items
			};
		}
		return this.controlgroup.apply( this, arguments );
	};
}

var widgetsButton = $.ui.button;


/*!
 * jQuery UI Dialog 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Dialog
//>>group: Widgets
//>>description: Displays customizable dialog windows.
//>>docs: http://api.jqueryui.com/dialog/
//>>demos: http://jqueryui.com/dialog/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/dialog.css
//>>css.theme: ../../themes/base/theme.css


$.widget( "ui.dialog", {
	version: "1.13.2",
	options: {
		appendTo: "body",
		autoOpen: true,
		buttons: [],
		classes: {
			"ui-dialog": "ui-corner-all",
			"ui-dialog-titlebar": "ui-corner-all"
		},
		closeOnEscape: true,
		closeText: "Close",
		draggable: true,
		hide: null,
		height: "auto",
		maxHeight: null,
		maxWidth: null,
		minHeight: 150,
		minWidth: 150,
		modal: false,
		position: {
			my: "center",
			at: "center",
			of: window,
			collision: "fit",

			// Ensure the titlebar is always visible
			using: function( pos ) {
				var topOffset = $( this ).css( pos ).offset().top;
				if ( topOffset < 0 ) {
					$( this ).css( "top", pos.top - topOffset );
				}
			}
		},
		resizable: true,
		show: null,
		title: null,
		width: 300,

		// Callbacks
		beforeClose: null,
		close: null,
		drag: null,
		dragStart: null,
		dragStop: null,
		focus: null,
		open: null,
		resize: null,
		resizeStart: null,
		resizeStop: null
	},

	sizeRelatedOptions: {
		buttons: true,
		height: true,
		maxHeight: true,
		maxWidth: true,
		minHeight: true,
		minWidth: true,
		width: true
	},

	resizableRelatedOptions: {
		maxHeight: true,
		maxWidth: true,
		minHeight: true,
		minWidth: true
	},

	_create: function() {
		this.originalCss = {
			display: this.element[ 0 ].style.display,
			width: this.element[ 0 ].style.width,
			minHeight: this.element[ 0 ].style.minHeight,
			maxHeight: this.element[ 0 ].style.maxHeight,
			height: this.element[ 0 ].style.height
		};
		this.originalPosition = {
			parent: this.element.parent(),
			index: this.element.parent().children().index( this.element )
		};
		this.originalTitle = this.element.attr( "title" );
		if ( this.options.title == null && this.originalTitle != null ) {
			this.options.title = this.originalTitle;
		}

		// Dialogs can't be disabled
		if ( this.options.disabled ) {
			this.options.disabled = false;
		}

		this._createWrapper();

		this.element
			.show()
			.removeAttr( "title" )
			.appendTo( this.uiDialog );

		this._addClass( "ui-dialog-content", "ui-widget-content" );

		this._createTitlebar();
		this._createButtonPane();

		if ( this.options.draggable && $.fn.draggable ) {
			this._makeDraggable();
		}
		if ( this.options.resizable && $.fn.resizable ) {
			this._makeResizable();
		}

		this._isOpen = false;

		this._trackFocus();
	},

	_init: function() {
		if ( this.options.autoOpen ) {
			this.open();
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;
		if ( element && ( element.jquery || element.nodeType ) ) {
			return $( element );
		}
		return this.document.find( element || "body" ).eq( 0 );
	},

	_destroy: function() {
		var next,
			originalPosition = this.originalPosition;

		this._untrackInstance();
		this._destroyOverlay();

		this.element
			.removeUniqueId()
			.css( this.originalCss )

			// Without detaching first, the following becomes really slow
			.detach();

		this.uiDialog.remove();

		if ( this.originalTitle ) {
			this.element.attr( "title", this.originalTitle );
		}

		next = originalPosition.parent.children().eq( originalPosition.index );

		// Don't try to place the dialog next to itself (#8613)
		if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
			next.before( this.element );
		} else {
			originalPosition.parent.append( this.element );
		}
	},

	widget: function() {
		return this.uiDialog;
	},

	disable: $.noop,
	enable: $.noop,

	close: function( event ) {
		var that = this;

		if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
			return;
		}

		this._isOpen = false;
		this._focusedElement = null;
		this._destroyOverlay();
		this._untrackInstance();

		if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) {

			// Hiding a focused element doesn't trigger blur in WebKit
			// so in case we have nothing to focus on, explicitly blur the active element
			// https://bugs.webkit.org/show_bug.cgi?id=47182
			$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
		}

		this._hide( this.uiDialog, this.options.hide, function() {
			that._trigger( "close", event );
		} );
	},

	isOpen: function() {
		return this._isOpen;
	},

	moveToTop: function() {
		this._moveToTop();
	},

	_moveToTop: function( event, silent ) {
		var moved = false,
			zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() {
				return +$( this ).css( "z-index" );
			} ).get(),
			zIndexMax = Math.max.apply( null, zIndices );

		if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
			this.uiDialog.css( "z-index", zIndexMax + 1 );
			moved = true;
		}

		if ( moved && !silent ) {
			this._trigger( "focus", event );
		}
		return moved;
	},

	open: function() {
		var that = this;
		if ( this._isOpen ) {
			if ( this._moveToTop() ) {
				this._focusTabbable();
			}
			return;
		}

		this._isOpen = true;
		this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );

		this._size();
		this._position();
		this._createOverlay();
		this._moveToTop( null, true );

		// Ensure the overlay is moved to the top with the dialog, but only when
		// opening. The overlay shouldn't move after the dialog is open so that
		// modeless dialogs opened after the modal dialog stack properly.
		if ( this.overlay ) {
			this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
		}

		this._show( this.uiDialog, this.options.show, function() {
			that._focusTabbable();
			that._trigger( "focus" );
		} );

		// Track the dialog immediately upon opening in case a focus event
		// somehow occurs outside of the dialog before an element inside the
		// dialog is focused (#10152)
		this._makeFocusTarget();

		this._trigger( "open" );
	},

	_focusTabbable: function() {

		// Set focus to the first match:
		// 1. An element that was focused previously
		// 2. First element inside the dialog matching [autofocus]
		// 3. Tabbable element inside the content element
		// 4. Tabbable element inside the buttonpane
		// 5. The close button
		// 6. The dialog itself
		var hasFocus = this._focusedElement;
		if ( !hasFocus ) {
			hasFocus = this.element.find( "[autofocus]" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.element.find( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialog;
		}
		hasFocus.eq( 0 ).trigger( "focus" );
	},

	_restoreTabbableFocus: function() {
		var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
			isActive = this.uiDialog[ 0 ] === activeElement ||
				$.contains( this.uiDialog[ 0 ], activeElement );
		if ( !isActive ) {
			this._focusTabbable();
		}
	},

	_keepFocus: function( event ) {
		event.preventDefault();
		this._restoreTabbableFocus();

		// support: IE
		// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
		// so we check again later
		this._delay( this._restoreTabbableFocus );
	},

	_createWrapper: function() {
		this.uiDialog = $( "<div>" )
			.hide()
			.attr( {

				// Setting tabIndex makes the div focusable
				tabIndex: -1,
				role: "dialog"
			} )
			.appendTo( this._appendTo() );

		this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
		this._on( this.uiDialog, {
			keydown: function( event ) {
				if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
						event.keyCode === $.ui.keyCode.ESCAPE ) {
					event.preventDefault();
					this.close( event );
					return;
				}

				// Prevent tabbing out of dialogs
				if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
					return;
				}
				var tabbables = this.uiDialog.find( ":tabbable" ),
					first = tabbables.first(),
					last = tabbables.last();

				if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) &&
						!event.shiftKey ) {
					this._delay( function() {
						first.trigger( "focus" );
					} );
					event.preventDefault();
				} else if ( ( event.target === first[ 0 ] ||
						event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) {
					this._delay( function() {
						last.trigger( "focus" );
					} );
					event.preventDefault();
				}
			},
			mousedown: function( event ) {
				if ( this._moveToTop( event ) ) {
					this._focusTabbable();
				}
			}
		} );

		// We assume that any existing aria-describedby attribute means
		// that the dialog content is marked up properly
		// otherwise we brute force the content as the description
		if ( !this.element.find( "[aria-describedby]" ).length ) {
			this.uiDialog.attr( {
				"aria-describedby": this.element.uniqueId().attr( "id" )
			} );
		}
	},

	_createTitlebar: function() {
		var uiDialogTitle;

		this.uiDialogTitlebar = $( "<div>" );
		this._addClass( this.uiDialogTitlebar,
			"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
		this._on( this.uiDialogTitlebar, {
			mousedown: function( event ) {

				// Don't prevent click on close button (#8838)
				// Focusing a dialog that is partially scrolled out of view
				// causes the browser to scroll it into view, preventing the click event
				if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {

					// Dialog isn't getting focus when dragging (#8063)
					this.uiDialog.trigger( "focus" );
				}
			}
		} );

		// Support: IE
		// Use type="button" to prevent enter keypresses in textboxes from closing the
		// dialog in IE (#9312)
		this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
			.button( {
				label: $( "<a>" ).text( this.options.closeText ).html(),
				icon: "ui-icon-closethick",
				showLabel: false
			} )
			.appendTo( this.uiDialogTitlebar );

		this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
		this._on( this.uiDialogTitlebarClose, {
			click: function( event ) {
				event.preventDefault();
				this.close( event );
			}
		} );

		uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
		this._addClass( uiDialogTitle, "ui-dialog-title" );
		this._title( uiDialogTitle );

		this.uiDialogTitlebar.prependTo( this.uiDialog );

		this.uiDialog.attr( {
			"aria-labelledby": uiDialogTitle.attr( "id" )
		} );
	},

	_title: function( title ) {
		if ( this.options.title ) {
			title.text( this.options.title );
		} else {
			title.html( "&#160;" );
		}
	},

	_createButtonPane: function() {
		this.uiDialogButtonPane = $( "<div>" );
		this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
			"ui-widget-content ui-helper-clearfix" );

		this.uiButtonSet = $( "<div>" )
			.appendTo( this.uiDialogButtonPane );
		this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );

		this._createButtons();
	},

	_createButtons: function() {
		var that = this,
			buttons = this.options.buttons;

		// If we already have a button pane, remove it
		this.uiDialogButtonPane.remove();
		this.uiButtonSet.empty();

		if ( $.isEmptyObject( buttons ) || ( Array.isArray( buttons ) && !buttons.length ) ) {
			this._removeClass( this.uiDialog, "ui-dialog-buttons" );
			return;
		}

		$.each( buttons, function( name, props ) {
			var click, buttonOptions;
			props = typeof props === "function" ?
				{ click: props, text: name } :
				props;

			// Default to a non-submitting button
			props = $.extend( { type: "button" }, props );

			// Change the context for the click callback to be the main element
			click = props.click;
			buttonOptions = {
				icon: props.icon,
				iconPosition: props.iconPosition,
				showLabel: props.showLabel,

				// Deprecated options
				icons: props.icons,
				text: props.text
			};

			delete props.click;
			delete props.icon;
			delete props.iconPosition;
			delete props.showLabel;

			// Deprecated options
			delete props.icons;
			if ( typeof props.text === "boolean" ) {
				delete props.text;
			}

			$( "<button></button>", props )
				.button( buttonOptions )
				.appendTo( that.uiButtonSet )
				.on( "click", function() {
					click.apply( that.element[ 0 ], arguments );
				} );
		} );
		this._addClass( this.uiDialog, "ui-dialog-buttons" );
		this.uiDialogButtonPane.appendTo( this.uiDialog );
	},

	_makeDraggable: function() {
		var that = this,
			options = this.options;

		function filteredUi( ui ) {
			return {
				position: ui.position,
				offset: ui.offset
			};
		}

		this.uiDialog.draggable( {
			cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
			handle: ".ui-dialog-titlebar",
			containment: "document",
			start: function( event, ui ) {
				that._addClass( $( this ), "ui-dialog-dragging" );
				that._blockFrames();
				that._trigger( "dragStart", event, filteredUi( ui ) );
			},
			drag: function( event, ui ) {
				that._trigger( "drag", event, filteredUi( ui ) );
			},
			stop: function( event, ui ) {
				var left = ui.offset.left - that.document.scrollLeft(),
					top = ui.offset.top - that.document.scrollTop();

				options.position = {
					my: "left top",
					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
						"top" + ( top >= 0 ? "+" : "" ) + top,
					of: that.window
				};
				that._removeClass( $( this ), "ui-dialog-dragging" );
				that._unblockFrames();
				that._trigger( "dragStop", event, filteredUi( ui ) );
			}
		} );
	},

	_makeResizable: function() {
		var that = this,
			options = this.options,
			handles = options.resizable,

			// .ui-resizable has position: relative defined in the stylesheet
			// but dialogs have to use absolute or fixed positioning
			position = this.uiDialog.css( "position" ),
			resizeHandles = typeof handles === "string" ?
				handles :
				"n,e,s,w,se,sw,ne,nw";

		function filteredUi( ui ) {
			return {
				originalPosition: ui.originalPosition,
				originalSize: ui.originalSize,
				position: ui.position,
				size: ui.size
			};
		}

		this.uiDialog.resizable( {
			cancel: ".ui-dialog-content",
			containment: "document",
			alsoResize: this.element,
			maxWidth: options.maxWidth,
			maxHeight: options.maxHeight,
			minWidth: options.minWidth,
			minHeight: this._minHeight(),
			handles: resizeHandles,
			start: function( event, ui ) {
				that._addClass( $( this ), "ui-dialog-resizing" );
				that._blockFrames();
				that._trigger( "resizeStart", event, filteredUi( ui ) );
			},
			resize: function( event, ui ) {
				that._trigger( "resize", event, filteredUi( ui ) );
			},
			stop: function( event, ui ) {
				var offset = that.uiDialog.offset(),
					left = offset.left - that.document.scrollLeft(),
					top = offset.top - that.document.scrollTop();

				options.height = that.uiDialog.height();
				options.width = that.uiDialog.width();
				options.position = {
					my: "left top",
					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
						"top" + ( top >= 0 ? "+" : "" ) + top,
					of: that.window
				};
				that._removeClass( $( this ), "ui-dialog-resizing" );
				that._unblockFrames();
				that._trigger( "resizeStop", event, filteredUi( ui ) );
			}
		} )
			.css( "position", position );
	},

	_trackFocus: function() {
		this._on( this.widget(), {
			focusin: function( event ) {
				this._makeFocusTarget();
				this._focusedElement = $( event.target );
			}
		} );
	},

	_makeFocusTarget: function() {
		this._untrackInstance();
		this._trackingInstances().unshift( this );
	},

	_untrackInstance: function() {
		var instances = this._trackingInstances(),
			exists = $.inArray( this, instances );
		if ( exists !== -1 ) {
			instances.splice( exists, 1 );
		}
	},

	_trackingInstances: function() {
		var instances = this.document.data( "ui-dialog-instances" );
		if ( !instances ) {
			instances = [];
			this.document.data( "ui-dialog-instances", instances );
		}
		return instances;
	},

	_minHeight: function() {
		var options = this.options;

		return options.height === "auto" ?
			options.minHeight :
			Math.min( options.minHeight, options.height );
	},

	_position: function() {

		// Need to show the dialog to get the actual offset in the position plugin
		var isVisible = this.uiDialog.is( ":visible" );
		if ( !isVisible ) {
			this.uiDialog.show();
		}
		this.uiDialog.position( this.options.position );
		if ( !isVisible ) {
			this.uiDialog.hide();
		}
	},

	_setOptions: function( options ) {
		var that = this,
			resize = false,
			resizableOptions = {};

		$.each( options, function( key, value ) {
			that._setOption( key, value );

			if ( key in that.sizeRelatedOptions ) {
				resize = true;
			}
			if ( key in that.resizableRelatedOptions ) {
				resizableOptions[ key ] = value;
			}
		} );

		if ( resize ) {
			this._size();
			this._position();
		}
		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
			this.uiDialog.resizable( "option", resizableOptions );
		}
	},

	_setOption: function( key, value ) {
		var isDraggable, isResizable,
			uiDialog = this.uiDialog;

		if ( key === "disabled" ) {
			return;
		}

		this._super( key, value );

		if ( key === "appendTo" ) {
			this.uiDialog.appendTo( this._appendTo() );
		}

		if ( key === "buttons" ) {
			this._createButtons();
		}

		if ( key === "closeText" ) {
			this.uiDialogTitlebarClose.button( {

				// Ensure that we always pass a string
				label: $( "<a>" ).text( "" + this.options.closeText ).html()
			} );
		}

		if ( key === "draggable" ) {
			isDraggable = uiDialog.is( ":data(ui-draggable)" );
			if ( isDraggable && !value ) {
				uiDialog.draggable( "destroy" );
			}

			if ( !isDraggable && value ) {
				this._makeDraggable();
			}
		}

		if ( key === "position" ) {
			this._position();
		}

		if ( key === "resizable" ) {

			// currently resizable, becoming non-resizable
			isResizable = uiDialog.is( ":data(ui-resizable)" );
			if ( isResizable && !value ) {
				uiDialog.resizable( "destroy" );
			}

			// Currently resizable, changing handles
			if ( isResizable && typeof value === "string" ) {
				uiDialog.resizable( "option", "handles", value );
			}

			// Currently non-resizable, becoming resizable
			if ( !isResizable && value !== false ) {
				this._makeResizable();
			}
		}

		if ( key === "title" ) {
			this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
		}
	},

	_size: function() {

		// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
		// divs will both have width and height set, so we need to reset them
		var nonContentHeight, minContentHeight, maxContentHeight,
			options = this.options;

		// Reset content sizing
		this.element.show().css( {
			width: "auto",
			minHeight: 0,
			maxHeight: "none",
			height: 0
		} );

		if ( options.minWidth > options.width ) {
			options.width = options.minWidth;
		}

		// Reset wrapper sizing
		// determine the height of all the non-content elements
		nonContentHeight = this.uiDialog.css( {
			height: "auto",
			width: options.width
		} )
			.outerHeight();
		minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
		maxContentHeight = typeof options.maxHeight === "number" ?
			Math.max( 0, options.maxHeight - nonContentHeight ) :
			"none";

		if ( options.height === "auto" ) {
			this.element.css( {
				minHeight: minContentHeight,
				maxHeight: maxContentHeight,
				height: "auto"
			} );
		} else {
			this.element.height( Math.max( 0, options.height - nonContentHeight ) );
		}

		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
			this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
		}
	},

	_blockFrames: function() {
		this.iframeBlocks = this.document.find( "iframe" ).map( function() {
			var iframe = $( this );

			return $( "<div>" )
				.css( {
					position: "absolute",
					width: iframe.outerWidth(),
					height: iframe.outerHeight()
				} )
				.appendTo( iframe.parent() )
				.offset( iframe.offset() )[ 0 ];
		} );
	},

	_unblockFrames: function() {
		if ( this.iframeBlocks ) {
			this.iframeBlocks.remove();
			delete this.iframeBlocks;
		}
	},

	_allowInteraction: function( event ) {
		if ( $( event.target ).closest( ".ui-dialog" ).length ) {
			return true;
		}

		// TODO: Remove hack when datepicker implements
		// the .ui-front logic (#8989)
		return !!$( event.target ).closest( ".ui-datepicker" ).length;
	},

	_createOverlay: function() {
		if ( !this.options.modal ) {
			return;
		}

		var jqMinor = $.fn.jquery.substring( 0, 4 );

		// We use a delay in case the overlay is created from an
		// event that we're going to be cancelling (#2804)
		var isOpening = true;
		this._delay( function() {
			isOpening = false;
		} );

		if ( !this.document.data( "ui-dialog-overlays" ) ) {

			// Prevent use of anchors and inputs
			// This doesn't use `_on()` because it is a shared event handler
			// across all open modal dialogs.
			this.document.on( "focusin.ui-dialog", function( event ) {
				if ( isOpening ) {
					return;
				}

				var instance = this._trackingInstances()[ 0 ];
				if ( !instance._allowInteraction( event ) ) {
					event.preventDefault();
					instance._focusTabbable();

					// Support: jQuery >=3.4 <3.6 only
					// Focus re-triggering in jQuery 3.4/3.5 makes the original element
					// have its focus event propagated last, breaking the re-targeting.
					// Trigger focus in a delay in addition if needed to avoid the issue
					// See https://github.com/jquery/jquery/issues/4382
					if ( jqMinor === "3.4." || jqMinor === "3.5." ) {
						instance._delay( instance._restoreTabbableFocus );
					}
				}
			}.bind( this ) );
		}

		this.overlay = $( "<div>" )
			.appendTo( this._appendTo() );

		this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
		this._on( this.overlay, {
			mousedown: "_keepFocus"
		} );
		this.document.data( "ui-dialog-overlays",
			( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 );
	},

	_destroyOverlay: function() {
		if ( !this.options.modal ) {
			return;
		}

		if ( this.overlay ) {
			var overlays = this.document.data( "ui-dialog-overlays" ) - 1;

			if ( !overlays ) {
				this.document.off( "focusin.ui-dialog" );
				this.document.removeData( "ui-dialog-overlays" );
			} else {
				this.document.data( "ui-dialog-overlays", overlays );
			}

			this.overlay.remove();
			this.overlay = null;
		}
	}
} );

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for dialogClass option
	$.widget( "ui.dialog", $.ui.dialog, {
		options: {
			dialogClass: ""
		},
		_createWrapper: function() {
			this._super();
			this.uiDialog.addClass( this.options.dialogClass );
		},
		_setOption: function( key, value ) {
			if ( key === "dialogClass" ) {
				this.uiDialog
					.removeClass( this.options.dialogClass )
					.addClass( value );
			}
			this._superApply( arguments );
		}
	} );
}

var widgetsDialog = $.ui.dialog;


/*!
 * jQuery UI Slider 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Slider
//>>group: Widgets
//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
//>>docs: http://api.jqueryui.com/slider/
//>>demos: http://jqueryui.com/slider/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/slider.css
//>>css.theme: ../../themes/base/theme.css


var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, {
	version: "1.13.2",
	widgetEventPrefix: "slide",

	options: {
		animate: false,
		classes: {
			"ui-slider": "ui-corner-all",
			"ui-slider-handle": "ui-corner-all",

			// Note: ui-widget-header isn't the most fittingly semantic framework class for this
			// element, but worked best visually with a variety of themes
			"ui-slider-range": "ui-corner-all ui-widget-header"
		},
		distance: 0,
		max: 100,
		min: 0,
		orientation: "horizontal",
		range: false,
		step: 1,
		value: 0,
		values: null,

		// Callbacks
		change: null,
		slide: null,
		start: null,
		stop: null
	},

	// Number of pages in a slider
	// (how many times can you page up/down to go through the whole range)
	numPages: 5,

	_create: function() {
		this._keySliding = false;
		this._mouseSliding = false;
		this._animateOff = true;
		this._handleIndex = null;
		this._detectOrientation();
		this._mouseInit();
		this._calculateNewMax();

		this._addClass( "ui-slider ui-slider-" + this.orientation,
			"ui-widget ui-widget-content" );

		this._refresh();

		this._animateOff = false;
	},

	_refresh: function() {
		this._createRange();
		this._createHandles();
		this._setupEvents();
		this._refreshValue();
	},

	_createHandles: function() {
		var i, handleCount,
			options = this.options,
			existingHandles = this.element.find( ".ui-slider-handle" ),
			handle = "<span tabindex='0'></span>",
			handles = [];

		handleCount = ( options.values && options.values.length ) || 1;

		if ( existingHandles.length > handleCount ) {
			existingHandles.slice( handleCount ).remove();
			existingHandles = existingHandles.slice( 0, handleCount );
		}

		for ( i = existingHandles.length; i < handleCount; i++ ) {
			handles.push( handle );
		}

		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );

		this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );

		this.handle = this.handles.eq( 0 );

		this.handles.each( function( i ) {
			$( this )
				.data( "ui-slider-handle-index", i )
				.attr( "tabIndex", 0 );
		} );
	},

	_createRange: function() {
		var options = this.options;

		if ( options.range ) {
			if ( options.range === true ) {
				if ( !options.values ) {
					options.values = [ this._valueMin(), this._valueMin() ];
				} else if ( options.values.length && options.values.length !== 2 ) {
					options.values = [ options.values[ 0 ], options.values[ 0 ] ];
				} else if ( Array.isArray( options.values ) ) {
					options.values = options.values.slice( 0 );
				}
			}

			if ( !this.range || !this.range.length ) {
				this.range = $( "<div>" )
					.appendTo( this.element );

				this._addClass( this.range, "ui-slider-range" );
			} else {
				this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );

				// Handle range switching from true to min/max
				this.range.css( {
					"left": "",
					"bottom": ""
				} );
			}
			if ( options.range === "min" || options.range === "max" ) {
				this._addClass( this.range, "ui-slider-range-" + options.range );
			}
		} else {
			if ( this.range ) {
				this.range.remove();
			}
			this.range = null;
		}
	},

	_setupEvents: function() {
		this._off( this.handles );
		this._on( this.handles, this._handleEvents );
		this._hoverable( this.handles );
		this._focusable( this.handles );
	},

	_destroy: function() {
		this.handles.remove();
		if ( this.range ) {
			this.range.remove();
		}

		this._mouseDestroy();
	},

	_mouseCapture: function( event ) {
		var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
			that = this,
			o = this.options;

		if ( o.disabled ) {
			return false;
		}

		this.elementSize = {
			width: this.element.outerWidth(),
			height: this.element.outerHeight()
		};
		this.elementOffset = this.element.offset();

		position = { x: event.pageX, y: event.pageY };
		normValue = this._normValueFromMouse( position );
		distance = this._valueMax() - this._valueMin() + 1;
		this.handles.each( function( i ) {
			var thisDistance = Math.abs( normValue - that.values( i ) );
			if ( ( distance > thisDistance ) ||
				( distance === thisDistance &&
					( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
				distance = thisDistance;
				closestHandle = $( this );
				index = i;
			}
		} );

		allowed = this._start( event, index );
		if ( allowed === false ) {
			return false;
		}
		this._mouseSliding = true;

		this._handleIndex = index;

		this._addClass( closestHandle, null, "ui-state-active" );
		closestHandle.trigger( "focus" );

		offset = closestHandle.offset();
		mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
			top: event.pageY - offset.top -
				( closestHandle.height() / 2 ) -
				( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
				( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
				( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
		};

		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
			this._slide( event, index, normValue );
		}
		this._animateOff = true;
		return true;
	},

	_mouseStart: function() {
		return true;
	},

	_mouseDrag: function( event ) {
		var position = { x: event.pageX, y: event.pageY },
			normValue = this._normValueFromMouse( position );

		this._slide( event, this._handleIndex, normValue );

		return false;
	},

	_mouseStop: function( event ) {
		this._removeClass( this.handles, null, "ui-state-active" );
		this._mouseSliding = false;

		this._stop( event, this._handleIndex );
		this._change( event, this._handleIndex );

		this._handleIndex = null;
		this._clickOffset = null;
		this._animateOff = false;

		return false;
	},

	_detectOrientation: function() {
		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
	},

	_normValueFromMouse: function( position ) {
		var pixelTotal,
			pixelMouse,
			percentMouse,
			valueTotal,
			valueMouse;

		if ( this.orientation === "horizontal" ) {
			pixelTotal = this.elementSize.width;
			pixelMouse = position.x - this.elementOffset.left -
				( this._clickOffset ? this._clickOffset.left : 0 );
		} else {
			pixelTotal = this.elementSize.height;
			pixelMouse = position.y - this.elementOffset.top -
				( this._clickOffset ? this._clickOffset.top : 0 );
		}

		percentMouse = ( pixelMouse / pixelTotal );
		if ( percentMouse > 1 ) {
			percentMouse = 1;
		}
		if ( percentMouse < 0 ) {
			percentMouse = 0;
		}
		if ( this.orientation === "vertical" ) {
			percentMouse = 1 - percentMouse;
		}

		valueTotal = this._valueMax() - this._valueMin();
		valueMouse = this._valueMin() + percentMouse * valueTotal;

		return this._trimAlignValue( valueMouse );
	},

	_uiHash: function( index, value, values ) {
		var uiHash = {
			handle: this.handles[ index ],
			handleIndex: index,
			value: value !== undefined ? value : this.value()
		};

		if ( this._hasMultipleValues() ) {
			uiHash.value = value !== undefined ? value : this.values( index );
			uiHash.values = values || this.values();
		}

		return uiHash;
	},

	_hasMultipleValues: function() {
		return this.options.values && this.options.values.length;
	},

	_start: function( event, index ) {
		return this._trigger( "start", event, this._uiHash( index ) );
	},

	_slide: function( event, index, newVal ) {
		var allowed, otherVal,
			currentValue = this.value(),
			newValues = this.values();

		if ( this._hasMultipleValues() ) {
			otherVal = this.values( index ? 0 : 1 );
			currentValue = this.values( index );

			if ( this.options.values.length === 2 && this.options.range === true ) {
				newVal =  index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
			}

			newValues[ index ] = newVal;
		}

		if ( newVal === currentValue ) {
			return;
		}

		allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );

		// A slide can be canceled by returning false from the slide callback
		if ( allowed === false ) {
			return;
		}

		if ( this._hasMultipleValues() ) {
			this.values( index, newVal );
		} else {
			this.value( newVal );
		}
	},

	_stop: function( event, index ) {
		this._trigger( "stop", event, this._uiHash( index ) );
	},

	_change: function( event, index ) {
		if ( !this._keySliding && !this._mouseSliding ) {

			//store the last changed value index for reference when handles overlap
			this._lastChangedValue = index;
			this._trigger( "change", event, this._uiHash( index ) );
		}
	},

	value: function( newValue ) {
		if ( arguments.length ) {
			this.options.value = this._trimAlignValue( newValue );
			this._refreshValue();
			this._change( null, 0 );
			return;
		}

		return this._value();
	},

	values: function( index, newValue ) {
		var vals,
			newValues,
			i;

		if ( arguments.length > 1 ) {
			this.options.values[ index ] = this._trimAlignValue( newValue );
			this._refreshValue();
			this._change( null, index );
			return;
		}

		if ( arguments.length ) {
			if ( Array.isArray( arguments[ 0 ] ) ) {
				vals = this.options.values;
				newValues = arguments[ 0 ];
				for ( i = 0; i < vals.length; i += 1 ) {
					vals[ i ] = this._trimAlignValue( newValues[ i ] );
					this._change( null, i );
				}
				this._refreshValue();
			} else {
				if ( this._hasMultipleValues() ) {
					return this._values( index );
				} else {
					return this.value();
				}
			}
		} else {
			return this._values();
		}
	},

	_setOption: function( key, value ) {
		var i,
			valsLength = 0;

		if ( key === "range" && this.options.range === true ) {
			if ( value === "min" ) {
				this.options.value = this._values( 0 );
				this.options.values = null;
			} else if ( value === "max" ) {
				this.options.value = this._values( this.options.values.length - 1 );
				this.options.values = null;
			}
		}

		if ( Array.isArray( this.options.values ) ) {
			valsLength = this.options.values.length;
		}

		this._super( key, value );

		switch ( key ) {
			case "orientation":
				this._detectOrientation();
				this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
					._addClass( "ui-slider-" + this.orientation );
				this._refreshValue();
				if ( this.options.range ) {
					this._refreshRange( value );
				}

				// Reset positioning from previous orientation
				this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
				break;
			case "value":
				this._animateOff = true;
				this._refreshValue();
				this._change( null, 0 );
				this._animateOff = false;
				break;
			case "values":
				this._animateOff = true;
				this._refreshValue();

				// Start from the last handle to prevent unreachable handles (#9046)
				for ( i = valsLength - 1; i >= 0; i-- ) {
					this._change( null, i );
				}
				this._animateOff = false;
				break;
			case "step":
			case "min":
			case "max":
				this._animateOff = true;
				this._calculateNewMax();
				this._refreshValue();
				this._animateOff = false;
				break;
			case "range":
				this._animateOff = true;
				this._refresh();
				this._animateOff = false;
				break;
		}
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this._toggleClass( null, "ui-state-disabled", !!value );
	},

	//internal value getter
	// _value() returns value trimmed by min and max, aligned by step
	_value: function() {
		var val = this.options.value;
		val = this._trimAlignValue( val );

		return val;
	},

	//internal values getter
	// _values() returns array of values trimmed by min and max, aligned by step
	// _values( index ) returns single value trimmed by min and max, aligned by step
	_values: function( index ) {
		var val,
			vals,
			i;

		if ( arguments.length ) {
			val = this.options.values[ index ];
			val = this._trimAlignValue( val );

			return val;
		} else if ( this._hasMultipleValues() ) {

			// .slice() creates a copy of the array
			// this copy gets trimmed by min and max and then returned
			vals = this.options.values.slice();
			for ( i = 0; i < vals.length; i += 1 ) {
				vals[ i ] = this._trimAlignValue( vals[ i ] );
			}

			return vals;
		} else {
			return [];
		}
	},

	// Returns the step-aligned value that val is closest to, between (inclusive) min and max
	_trimAlignValue: function( val ) {
		if ( val <= this._valueMin() ) {
			return this._valueMin();
		}
		if ( val >= this._valueMax() ) {
			return this._valueMax();
		}
		var step = ( this.options.step > 0 ) ? this.options.step : 1,
			valModStep = ( val - this._valueMin() ) % step,
			alignValue = val - valModStep;

		if ( Math.abs( valModStep ) * 2 >= step ) {
			alignValue += ( valModStep > 0 ) ? step : ( -step );
		}

		// Since JavaScript has problems with large floats, round
		// the final value to 5 digits after the decimal point (see #4124)
		return parseFloat( alignValue.toFixed( 5 ) );
	},

	_calculateNewMax: function() {
		var max = this.options.max,
			min = this._valueMin(),
			step = this.options.step,
			aboveMin = Math.round( ( max - min ) / step ) * step;
		max = aboveMin + min;
		if ( max > this.options.max ) {

			//If max is not divisible by step, rounding off may increase its value
			max -= step;
		}
		this.max = parseFloat( max.toFixed( this._precision() ) );
	},

	_precision: function() {
		var precision = this._precisionOf( this.options.step );
		if ( this.options.min !== null ) {
			precision = Math.max( precision, this._precisionOf( this.options.min ) );
		}
		return precision;
	},

	_precisionOf: function( num ) {
		var str = num.toString(),
			decimal = str.indexOf( "." );
		return decimal === -1 ? 0 : str.length - decimal - 1;
	},

	_valueMin: function() {
		return this.options.min;
	},

	_valueMax: function() {
		return this.max;
	},

	_refreshRange: function( orientation ) {
		if ( orientation === "vertical" ) {
			this.range.css( { "width": "", "left": "" } );
		}
		if ( orientation === "horizontal" ) {
			this.range.css( { "height": "", "bottom": "" } );
		}
	},

	_refreshValue: function() {
		var lastValPercent, valPercent, value, valueMin, valueMax,
			oRange = this.options.range,
			o = this.options,
			that = this,
			animate = ( !this._animateOff ) ? o.animate : false,
			_set = {};

		if ( this._hasMultipleValues() ) {
			this.handles.each( function( i ) {
				valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() -
					that._valueMin() ) * 100;
				_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
				if ( that.options.range === true ) {
					if ( that.orientation === "horizontal" ) {
						if ( i === 0 ) {
							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
								left: valPercent + "%"
							}, o.animate );
						}
						if ( i === 1 ) {
							that.range[ animate ? "animate" : "css" ]( {
								width: ( valPercent - lastValPercent ) + "%"
							}, {
								queue: false,
								duration: o.animate
							} );
						}
					} else {
						if ( i === 0 ) {
							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
								bottom: ( valPercent ) + "%"
							}, o.animate );
						}
						if ( i === 1 ) {
							that.range[ animate ? "animate" : "css" ]( {
								height: ( valPercent - lastValPercent ) + "%"
							}, {
								queue: false,
								duration: o.animate
							} );
						}
					}
				}
				lastValPercent = valPercent;
			} );
		} else {
			value = this.value();
			valueMin = this._valueMin();
			valueMax = this._valueMax();
			valPercent = ( valueMax !== valueMin ) ?
					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
					0;
			_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );

			if ( oRange === "min" && this.orientation === "horizontal" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					width: valPercent + "%"
				}, o.animate );
			}
			if ( oRange === "max" && this.orientation === "horizontal" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					width: ( 100 - valPercent ) + "%"
				}, o.animate );
			}
			if ( oRange === "min" && this.orientation === "vertical" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					height: valPercent + "%"
				}, o.animate );
			}
			if ( oRange === "max" && this.orientation === "vertical" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					height: ( 100 - valPercent ) + "%"
				}, o.animate );
			}
		}
	},

	_handleEvents: {
		keydown: function( event ) {
			var allowed, curVal, newVal, step,
				index = $( event.target ).data( "ui-slider-handle-index" );

			switch ( event.keyCode ) {
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_UP:
				case $.ui.keyCode.PAGE_DOWN:
				case $.ui.keyCode.UP:
				case $.ui.keyCode.RIGHT:
				case $.ui.keyCode.DOWN:
				case $.ui.keyCode.LEFT:
					event.preventDefault();
					if ( !this._keySliding ) {
						this._keySliding = true;
						this._addClass( $( event.target ), null, "ui-state-active" );
						allowed = this._start( event, index );
						if ( allowed === false ) {
							return;
						}
					}
					break;
			}

			step = this.options.step;
			if ( this._hasMultipleValues() ) {
				curVal = newVal = this.values( index );
			} else {
				curVal = newVal = this.value();
			}

			switch ( event.keyCode ) {
				case $.ui.keyCode.HOME:
					newVal = this._valueMin();
					break;
				case $.ui.keyCode.END:
					newVal = this._valueMax();
					break;
				case $.ui.keyCode.PAGE_UP:
					newVal = this._trimAlignValue(
						curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
					);
					break;
				case $.ui.keyCode.PAGE_DOWN:
					newVal = this._trimAlignValue(
						curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) );
					break;
				case $.ui.keyCode.UP:
				case $.ui.keyCode.RIGHT:
					if ( curVal === this._valueMax() ) {
						return;
					}
					newVal = this._trimAlignValue( curVal + step );
					break;
				case $.ui.keyCode.DOWN:
				case $.ui.keyCode.LEFT:
					if ( curVal === this._valueMin() ) {
						return;
					}
					newVal = this._trimAlignValue( curVal - step );
					break;
			}

			this._slide( event, index, newVal );
		},
		keyup: function( event ) {
			var index = $( event.target ).data( "ui-slider-handle-index" );

			if ( this._keySliding ) {
				this._keySliding = false;
				this._stop( event, index );
				this._change( event, index );
				this._removeClass( $( event.target ), null, "ui-state-active" );
			}
		}
	}
} );


/*!
 * jQuery UI Tabs 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Tabs
//>>group: Widgets
//>>description: Transforms a set of container elements into a tab structure.
//>>docs: http://api.jqueryui.com/tabs/
//>>demos: http://jqueryui.com/tabs/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/tabs.css
//>>css.theme: ../../themes/base/theme.css


$.widget( "ui.tabs", {
	version: "1.13.2",
	delay: 300,
	options: {
		active: null,
		classes: {
			"ui-tabs": "ui-corner-all",
			"ui-tabs-nav": "ui-corner-all",
			"ui-tabs-panel": "ui-corner-bottom",
			"ui-tabs-tab": "ui-corner-top"
		},
		collapsible: false,
		event: "click",
		heightStyle: "content",
		hide: null,
		show: null,

		// Callbacks
		activate: null,
		beforeActivate: null,
		beforeLoad: null,
		load: null
	},

	_isLocal: ( function() {
		var rhash = /#.*$/;

		return function( anchor ) {
			var anchorUrl, locationUrl;

			anchorUrl = anchor.href.replace( rhash, "" );
			locationUrl = location.href.replace( rhash, "" );

			// Decoding may throw an error if the URL isn't UTF-8 (#9518)
			try {
				anchorUrl = decodeURIComponent( anchorUrl );
			} catch ( error ) {}
			try {
				locationUrl = decodeURIComponent( locationUrl );
			} catch ( error ) {}

			return anchor.hash.length > 1 && anchorUrl === locationUrl;
		};
	} )(),

	_create: function() {
		var that = this,
			options = this.options;

		this.running = false;

		this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
		this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );

		this._processTabs();
		options.active = this._initialActive();

		// Take disabling tabs via class attribute from HTML
		// into account and update option properly.
		if ( Array.isArray( options.disabled ) ) {
			options.disabled = $.uniqueSort( options.disabled.concat(
				$.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
					return that.tabs.index( li );
				} )
			) ).sort();
		}

		// Check for length avoids error when initializing empty list
		if ( this.options.active !== false && this.anchors.length ) {
			this.active = this._findActive( options.active );
		} else {
			this.active = $();
		}

		this._refresh();

		if ( this.active.length ) {
			this.load( options.active );
		}
	},

	_initialActive: function() {
		var active = this.options.active,
			collapsible = this.options.collapsible,
			locationHash = location.hash.substring( 1 );

		if ( active === null ) {

			// check the fragment identifier in the URL
			if ( locationHash ) {
				this.tabs.each( function( i, tab ) {
					if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
						active = i;
						return false;
					}
				} );
			}

			// Check for a tab marked active via a class
			if ( active === null ) {
				active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
			}

			// No active tab, set to false
			if ( active === null || active === -1 ) {
				active = this.tabs.length ? 0 : false;
			}
		}

		// Handle numbers: negative, out of range
		if ( active !== false ) {
			active = this.tabs.index( this.tabs.eq( active ) );
			if ( active === -1 ) {
				active = collapsible ? false : 0;
			}
		}

		// Don't allow collapsible: false and active: false
		if ( !collapsible && active === false && this.anchors.length ) {
			active = 0;
		}

		return active;
	},

	_getCreateEventData: function() {
		return {
			tab: this.active,
			panel: !this.active.length ? $() : this._getPanelForTab( this.active )
		};
	},

	_tabKeydown: function( event ) {
		var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
			selectedIndex = this.tabs.index( focusedTab ),
			goingForward = true;

		if ( this._handlePageNav( event ) ) {
			return;
		}

		switch ( event.keyCode ) {
		case $.ui.keyCode.RIGHT:
		case $.ui.keyCode.DOWN:
			selectedIndex++;
			break;
		case $.ui.keyCode.UP:
		case $.ui.keyCode.LEFT:
			goingForward = false;
			selectedIndex--;
			break;
		case $.ui.keyCode.END:
			selectedIndex = this.anchors.length - 1;
			break;
		case $.ui.keyCode.HOME:
			selectedIndex = 0;
			break;
		case $.ui.keyCode.SPACE:

			// Activate only, no collapsing
			event.preventDefault();
			clearTimeout( this.activating );
			this._activate( selectedIndex );
			return;
		case $.ui.keyCode.ENTER:

			// Toggle (cancel delayed activation, allow collapsing)
			event.preventDefault();
			clearTimeout( this.activating );

			// Determine if we should collapse or activate
			this._activate( selectedIndex === this.options.active ? false : selectedIndex );
			return;
		default:
			return;
		}

		// Focus the appropriate tab, based on which key was pressed
		event.preventDefault();
		clearTimeout( this.activating );
		selectedIndex = this._focusNextTab( selectedIndex, goingForward );

		// Navigating with control/command key will prevent automatic activation
		if ( !event.ctrlKey && !event.metaKey ) {

			// Update aria-selected immediately so that AT think the tab is already selected.
			// Otherwise AT may confuse the user by stating that they need to activate the tab,
			// but the tab will already be activated by the time the announcement finishes.
			focusedTab.attr( "aria-selected", "false" );
			this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );

			this.activating = this._delay( function() {
				this.option( "active", selectedIndex );
			}, this.delay );
		}
	},

	_panelKeydown: function( event ) {
		if ( this._handlePageNav( event ) ) {
			return;
		}

		// Ctrl+up moves focus to the current tab
		if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
			event.preventDefault();
			this.active.trigger( "focus" );
		}
	},

	// Alt+page up/down moves focus to the previous/next tab (and activates)
	_handlePageNav: function( event ) {
		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
			this._activate( this._focusNextTab( this.options.active - 1, false ) );
			return true;
		}
		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
			this._activate( this._focusNextTab( this.options.active + 1, true ) );
			return true;
		}
	},

	_findNextTab: function( index, goingForward ) {
		var lastTabIndex = this.tabs.length - 1;

		function constrain() {
			if ( index > lastTabIndex ) {
				index = 0;
			}
			if ( index < 0 ) {
				index = lastTabIndex;
			}
			return index;
		}

		while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
			index = goingForward ? index + 1 : index - 1;
		}

		return index;
	},

	_focusNextTab: function( index, goingForward ) {
		index = this._findNextTab( index, goingForward );
		this.tabs.eq( index ).trigger( "focus" );
		return index;
	},

	_setOption: function( key, value ) {
		if ( key === "active" ) {

			// _activate() will handle invalid values and update this.options
			this._activate( value );
			return;
		}

		this._super( key, value );

		if ( key === "collapsible" ) {
			this._toggleClass( "ui-tabs-collapsible", null, value );

			// Setting collapsible: false while collapsed; open first panel
			if ( !value && this.options.active === false ) {
				this._activate( 0 );
			}
		}

		if ( key === "event" ) {
			this._setupEvents( value );
		}

		if ( key === "heightStyle" ) {
			this._setupHeightStyle( value );
		}
	},

	_sanitizeSelector: function( hash ) {
		return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
	},

	refresh: function() {
		var options = this.options,
			lis = this.tablist.children( ":has(a[href])" );

		// Get disabled tabs from class attribute from HTML
		// this will get converted to a boolean if needed in _refresh()
		options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
			return lis.index( tab );
		} );

		this._processTabs();

		// Was collapsed or no tabs
		if ( options.active === false || !this.anchors.length ) {
			options.active = false;
			this.active = $();

		// was active, but active tab is gone
		} else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {

			// all remaining tabs are disabled
			if ( this.tabs.length === options.disabled.length ) {
				options.active = false;
				this.active = $();

			// activate previous tab
			} else {
				this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
			}

		// was active, active tab still exists
		} else {

			// make sure active index is correct
			options.active = this.tabs.index( this.active );
		}

		this._refresh();
	},

	_refresh: function() {
		this._setOptionDisabled( this.options.disabled );
		this._setupEvents( this.options.event );
		this._setupHeightStyle( this.options.heightStyle );

		this.tabs.not( this.active ).attr( {
			"aria-selected": "false",
			"aria-expanded": "false",
			tabIndex: -1
		} );
		this.panels.not( this._getPanelForTab( this.active ) )
			.hide()
			.attr( {
				"aria-hidden": "true"
			} );

		// Make sure one tab is in the tab order
		if ( !this.active.length ) {
			this.tabs.eq( 0 ).attr( "tabIndex", 0 );
		} else {
			this.active
				.attr( {
					"aria-selected": "true",
					"aria-expanded": "true",
					tabIndex: 0
				} );
			this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
			this._getPanelForTab( this.active )
				.show()
				.attr( {
					"aria-hidden": "false"
				} );
		}
	},

	_processTabs: function() {
		var that = this,
			prevTabs = this.tabs,
			prevAnchors = this.anchors,
			prevPanels = this.panels;

		this.tablist = this._getList().attr( "role", "tablist" );
		this._addClass( this.tablist, "ui-tabs-nav",
			"ui-helper-reset ui-helper-clearfix ui-widget-header" );

		// Prevent users from focusing disabled tabs via click
		this.tablist
			.on( "mousedown" + this.eventNamespace, "> li", function( event ) {
				if ( $( this ).is( ".ui-state-disabled" ) ) {
					event.preventDefault();
				}
			} )

			// Support: IE <9
			// Preventing the default action in mousedown doesn't prevent IE
			// from focusing the element, so if the anchor gets focused, blur.
			// We don't have to worry about focusing the previously focused
			// element since clicking on a non-focusable element should focus
			// the body anyway.
			.on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
				if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
					this.blur();
				}
			} );

		this.tabs = this.tablist.find( "> li:has(a[href])" )
			.attr( {
				role: "tab",
				tabIndex: -1
			} );
		this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );

		this.anchors = this.tabs.map( function() {
			return $( "a", this )[ 0 ];
		} )
			.attr( {
				tabIndex: -1
			} );
		this._addClass( this.anchors, "ui-tabs-anchor" );

		this.panels = $();

		this.anchors.each( function( i, anchor ) {
			var selector, panel, panelId,
				anchorId = $( anchor ).uniqueId().attr( "id" ),
				tab = $( anchor ).closest( "li" ),
				originalAriaControls = tab.attr( "aria-controls" );

			// Inline tab
			if ( that._isLocal( anchor ) ) {
				selector = anchor.hash;
				panelId = selector.substring( 1 );
				panel = that.element.find( that._sanitizeSelector( selector ) );

			// remote tab
			} else {

				// If the tab doesn't already have aria-controls,
				// generate an id by using a throw-away element
				panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
				selector = "#" + panelId;
				panel = that.element.find( selector );
				if ( !panel.length ) {
					panel = that._createPanel( panelId );
					panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
				}
				panel.attr( "aria-live", "polite" );
			}

			if ( panel.length ) {
				that.panels = that.panels.add( panel );
			}
			if ( originalAriaControls ) {
				tab.data( "ui-tabs-aria-controls", originalAriaControls );
			}
			tab.attr( {
				"aria-controls": panelId,
				"aria-labelledby": anchorId
			} );
			panel.attr( "aria-labelledby", anchorId );
		} );

		this.panels.attr( "role", "tabpanel" );
		this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );

		// Avoid memory leaks (#10056)
		if ( prevTabs ) {
			this._off( prevTabs.not( this.tabs ) );
			this._off( prevAnchors.not( this.anchors ) );
			this._off( prevPanels.not( this.panels ) );
		}
	},

	// Allow overriding how to find the list for rare usage scenarios (#7715)
	_getList: function() {
		return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
	},

	_createPanel: function( id ) {
		return $( "<div>" )
			.attr( "id", id )
			.data( "ui-tabs-destroy", true );
	},

	_setOptionDisabled: function( disabled ) {
		var currentItem, li, i;

		if ( Array.isArray( disabled ) ) {
			if ( !disabled.length ) {
				disabled = false;
			} else if ( disabled.length === this.anchors.length ) {
				disabled = true;
			}
		}

		// Disable tabs
		for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
			currentItem = $( li );
			if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
				currentItem.attr( "aria-disabled", "true" );
				this._addClass( currentItem, null, "ui-state-disabled" );
			} else {
				currentItem.removeAttr( "aria-disabled" );
				this._removeClass( currentItem, null, "ui-state-disabled" );
			}
		}

		this.options.disabled = disabled;

		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
			disabled === true );
	},

	_setupEvents: function( event ) {
		var events = {};
		if ( event ) {
			$.each( event.split( " " ), function( index, eventName ) {
				events[ eventName ] = "_eventHandler";
			} );
		}

		this._off( this.anchors.add( this.tabs ).add( this.panels ) );

		// Always prevent the default action, even when disabled
		this._on( true, this.anchors, {
			click: function( event ) {
				event.preventDefault();
			}
		} );
		this._on( this.anchors, events );
		this._on( this.tabs, { keydown: "_tabKeydown" } );
		this._on( this.panels, { keydown: "_panelKeydown" } );

		this._focusable( this.tabs );
		this._hoverable( this.tabs );
	},

	_setupHeightStyle: function( heightStyle ) {
		var maxHeight,
			parent = this.element.parent();

		if ( heightStyle === "fill" ) {
			maxHeight = parent.height();
			maxHeight -= this.element.outerHeight() - this.element.height();

			this.element.siblings( ":visible" ).each( function() {
				var elem = $( this ),
					position = elem.css( "position" );

				if ( position === "absolute" || position === "fixed" ) {
					return;
				}
				maxHeight -= elem.outerHeight( true );
			} );

			this.element.children().not( this.panels ).each( function() {
				maxHeight -= $( this ).outerHeight( true );
			} );

			this.panels.each( function() {
				$( this ).height( Math.max( 0, maxHeight -
					$( this ).innerHeight() + $( this ).height() ) );
			} )
				.css( "overflow", "auto" );
		} else if ( heightStyle === "auto" ) {
			maxHeight = 0;
			this.panels.each( function() {
				maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
			} ).height( maxHeight );
		}
	},

	_eventHandler: function( event ) {
		var options = this.options,
			active = this.active,
			anchor = $( event.currentTarget ),
			tab = anchor.closest( "li" ),
			clickedIsActive = tab[ 0 ] === active[ 0 ],
			collapsing = clickedIsActive && options.collapsible,
			toShow = collapsing ? $() : this._getPanelForTab( tab ),
			toHide = !active.length ? $() : this._getPanelForTab( active ),
			eventData = {
				oldTab: active,
				oldPanel: toHide,
				newTab: collapsing ? $() : tab,
				newPanel: toShow
			};

		event.preventDefault();

		if ( tab.hasClass( "ui-state-disabled" ) ||

				// tab is already loading
				tab.hasClass( "ui-tabs-loading" ) ||

				// can't switch durning an animation
				this.running ||

				// click on active header, but not collapsible
				( clickedIsActive && !options.collapsible ) ||

				// allow canceling activation
				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
			return;
		}

		options.active = collapsing ? false : this.tabs.index( tab );

		this.active = clickedIsActive ? $() : tab;
		if ( this.xhr ) {
			this.xhr.abort();
		}

		if ( !toHide.length && !toShow.length ) {
			$.error( "jQuery UI Tabs: Mismatching fragment identifier." );
		}

		if ( toShow.length ) {
			this.load( this.tabs.index( tab ), event );
		}
		this._toggle( event, eventData );
	},

	// Handles show/hide for selecting tabs
	_toggle: function( event, eventData ) {
		var that = this,
			toShow = eventData.newPanel,
			toHide = eventData.oldPanel;

		this.running = true;

		function complete() {
			that.running = false;
			that._trigger( "activate", event, eventData );
		}

		function show() {
			that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );

			if ( toShow.length && that.options.show ) {
				that._show( toShow, that.options.show, complete );
			} else {
				toShow.show();
				complete();
			}
		}

		// Start out by hiding, then showing, then completing
		if ( toHide.length && this.options.hide ) {
			this._hide( toHide, this.options.hide, function() {
				that._removeClass( eventData.oldTab.closest( "li" ),
					"ui-tabs-active", "ui-state-active" );
				show();
			} );
		} else {
			this._removeClass( eventData.oldTab.closest( "li" ),
				"ui-tabs-active", "ui-state-active" );
			toHide.hide();
			show();
		}

		toHide.attr( "aria-hidden", "true" );
		eventData.oldTab.attr( {
			"aria-selected": "false",
			"aria-expanded": "false"
		} );

		// If we're switching tabs, remove the old tab from the tab order.
		// If we're opening from collapsed state, remove the previous tab from the tab order.
		// If we're collapsing, then keep the collapsing tab in the tab order.
		if ( toShow.length && toHide.length ) {
			eventData.oldTab.attr( "tabIndex", -1 );
		} else if ( toShow.length ) {
			this.tabs.filter( function() {
				return $( this ).attr( "tabIndex" ) === 0;
			} )
				.attr( "tabIndex", -1 );
		}

		toShow.attr( "aria-hidden", "false" );
		eventData.newTab.attr( {
			"aria-selected": "true",
			"aria-expanded": "true",
			tabIndex: 0
		} );
	},

	_activate: function( index ) {
		var anchor,
			active = this._findActive( index );

		// Trying to activate the already active panel
		if ( active[ 0 ] === this.active[ 0 ] ) {
			return;
		}

		// Trying to collapse, simulate a click on the current active header
		if ( !active.length ) {
			active = this.active;
		}

		anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
		this._eventHandler( {
			target: anchor,
			currentTarget: anchor,
			preventDefault: $.noop
		} );
	},

	_findActive: function( index ) {
		return index === false ? $() : this.tabs.eq( index );
	},

	_getIndex: function( index ) {

		// meta-function to give users option to provide a href string instead of a numerical index.
		if ( typeof index === "string" ) {
			index = this.anchors.index( this.anchors.filter( "[href$='" +
				$.escapeSelector( index ) + "']" ) );
		}

		return index;
	},

	_destroy: function() {
		if ( this.xhr ) {
			this.xhr.abort();
		}

		this.tablist
			.removeAttr( "role" )
			.off( this.eventNamespace );

		this.anchors
			.removeAttr( "role tabIndex" )
			.removeUniqueId();

		this.tabs.add( this.panels ).each( function() {
			if ( $.data( this, "ui-tabs-destroy" ) ) {
				$( this ).remove();
			} else {
				$( this ).removeAttr( "role tabIndex " +
					"aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
			}
		} );

		this.tabs.each( function() {
			var li = $( this ),
				prev = li.data( "ui-tabs-aria-controls" );
			if ( prev ) {
				li
					.attr( "aria-controls", prev )
					.removeData( "ui-tabs-aria-controls" );
			} else {
				li.removeAttr( "aria-controls" );
			}
		} );

		this.panels.show();

		if ( this.options.heightStyle !== "content" ) {
			this.panels.css( "height", "" );
		}
	},

	enable: function( index ) {
		var disabled = this.options.disabled;
		if ( disabled === false ) {
			return;
		}

		if ( index === undefined ) {
			disabled = false;
		} else {
			index = this._getIndex( index );
			if ( Array.isArray( disabled ) ) {
				disabled = $.map( disabled, function( num ) {
					return num !== index ? num : null;
				} );
			} else {
				disabled = $.map( this.tabs, function( li, num ) {
					return num !== index ? num : null;
				} );
			}
		}
		this._setOptionDisabled( disabled );
	},

	disable: function( index ) {
		var disabled = this.options.disabled;
		if ( disabled === true ) {
			return;
		}

		if ( index === undefined ) {
			disabled = true;
		} else {
			index = this._getIndex( index );
			if ( $.inArray( index, disabled ) !== -1 ) {
				return;
			}
			if ( Array.isArray( disabled ) ) {
				disabled = $.merge( [ index ], disabled ).sort();
			} else {
				disabled = [ index ];
			}
		}
		this._setOptionDisabled( disabled );
	},

	load: function( index, event ) {
		index = this._getIndex( index );
		var that = this,
			tab = this.tabs.eq( index ),
			anchor = tab.find( ".ui-tabs-anchor" ),
			panel = this._getPanelForTab( tab ),
			eventData = {
				tab: tab,
				panel: panel
			},
			complete = function( jqXHR, status ) {
				if ( status === "abort" ) {
					that.panels.stop( false, true );
				}

				that._removeClass( tab, "ui-tabs-loading" );
				panel.removeAttr( "aria-busy" );

				if ( jqXHR === that.xhr ) {
					delete that.xhr;
				}
			};

		// Not remote
		if ( this._isLocal( anchor[ 0 ] ) ) {
			return;
		}

		this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );

		// Support: jQuery <1.8
		// jQuery <1.8 returns false if the request is canceled in beforeSend,
		// but as of 1.8, $.ajax() always returns a jqXHR object.
		if ( this.xhr && this.xhr.statusText !== "canceled" ) {
			this._addClass( tab, "ui-tabs-loading" );
			panel.attr( "aria-busy", "true" );

			this.xhr
				.done( function( response, status, jqXHR ) {

					// support: jQuery <1.8
					// http://bugs.jquery.com/ticket/11778
					setTimeout( function() {
						panel.html( response );
						that._trigger( "load", event, eventData );

						complete( jqXHR, status );
					}, 1 );
				} )
				.fail( function( jqXHR, status ) {

					// support: jQuery <1.8
					// http://bugs.jquery.com/ticket/11778
					setTimeout( function() {
						complete( jqXHR, status );
					}, 1 );
				} );
		}
	},

	_ajaxSettings: function( anchor, event, eventData ) {
		var that = this;
		return {

			// Support: IE <11 only
			// Strip any hash that exists to prevent errors with the Ajax request
			url: anchor.attr( "href" ).replace( /#.*$/, "" ),
			beforeSend: function( jqXHR, settings ) {
				return that._trigger( "beforeLoad", event,
					$.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
			}
		};
	},

	_getPanelForTab: function( tab ) {
		var id = $( tab ).attr( "aria-controls" );
		return this.element.find( this._sanitizeSelector( "#" + id ) );
	}
} );

// DEPRECATED
// TODO: Switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for ui-tab class (now ui-tabs-tab)
	$.widget( "ui.tabs", $.ui.tabs, {
		_processTabs: function() {
			this._superApply( arguments );
			this._addClass( this.tabs, "ui-tab" );
		}
	} );
}

var widgetsTabs = $.ui.tabs;




} );

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('uiRegistry',[
    'jquery',
    'underscore'
], function ($, _) {
    'use strict';

    var privateData = new WeakMap();

    /**
     * Extracts private item storage associated
     * with a provided registry instance.
     *
     * @param {Object} container
     * @returns {Object}
     */
    function getItems(container) {
        return privateData.get(container).items;
    }

    /**
     * Extracts private requests array associated
     * with a provided registry instance.
     *
     * @param {Object} container
     * @returns {Array}
     */
    function getRequests(container) {
        return privateData.get(container).requests;
    }

    /**
     * Wrapper function used for convenient access to the elements.
     * See 'async' method for examples of usage and comparison
     * with a regular 'get' method.
     *
     * @param {(String|Object|Function)} name - Key of the requested element.
     * @param {Registry} registry - Instance of a registry
     *      where to search for the element.
     * @param {(Function|String)} [method] - Optional callback function
     *      or a name of the elements' method which
     *      will be invoked when element is available in registry.
     * @returns {*}
     */
    function async(name, registry, method) {
        var args = _.toArray(arguments).slice(3);

        if (_.isString(method)) {
            registry.get(name, function (component) {
                component[method].apply(component, args);
            });
        } else if (_.isFunction(method)) {
            registry.get(name, method);
        } else if (!args.length) {
            return registry.get(name);
        }
    }

    /**
     * Checks that every property of the query object
     * is present and equal to the corresponding
     * property in target object.
     * Note that non-strict comparison is used.
     *
     * @param {Object} query - Query object.
     * @param {Object} target - Target object.
     * @returns {Boolean}
     */
    function compare(query, target) {
        var matches = true,
            index,
            keys,
            key;

        if (!_.isObject(query) || !_.isObject(target)) {
            return false;
        }

        keys = Object.getOwnPropertyNames(query);
        index = keys.length;

        while (matches && index--) {
            key = keys[index];

            /* eslint-disable eqeqeq */
            if (target[key] != query[key]) {
                matches = false;
            }

            /* eslint-enable eqeqeq */
        }

        return matches;
    }

    /**
     * Explodes incoming string into object if
     * string is defined as a set of key = value pairs.
     *
     * @param {(String|*)} query - String to be processed.
     * @returns {Object|*} Either created object or an unmodified incoming
     *      value if conversion was not possible.
     * @example Sample conversions.
     *      'key = value, key2 = value2'
     *      => {key: 'value', key2: 'value2'}
     */
    function explode(query) {
        var result = {},
            index,
            data;

        if (typeof query !== 'string' || !~query.indexOf('=')) {
            return query;
        }

        query = query.split(',');
        index = query.length;

        while (index--) {
            data = query[index].split('=');

            result[data[0].trim()] = data[1].trim();
        }

        return result;
    }

    /**
     * Extracts items from the provided data object
     * which matches specified search criteria.
     *
     * @param {Object} data - Data object where to perform a lookup.
     * @param {(String|Object|Function)} query - Search criteria.
     * @param {Boolean} findAll - Flag that defines whether to
     *      search for all applicable items or to stop on a first found entry.
     * @returns {Array|Object|*}
     */
    function find(data, query, findAll) {
        var iterator,
            item;

        query = explode(query);

        if (typeof query === 'string') {
            item = data[query];

            if (findAll) {
                return item ? [item] : [];
            }

            return item;
        }

        iterator = !_.isFunction(query) ?
            compare.bind(null, query) :
            query;

        return findAll ?
            _.filter(data, iterator) :
            _.find(data, iterator);
    }

    /**
     * @constructor
     */
    function Registry() {
        var data = {
            items: {},
            requests: []
        };

        this._updateRequests = _.debounce(this._updateRequests.bind(this), 10);
        privateData.set(this, data);
    }

    Registry.prototype = {
        constructor: Registry,

        /**
         * Retrieves item from registry which matches specified search criteria.
         *
         * @param {(Object|String|Function|Array)} query - Search condition (see examples).
         * @param {Function} [callback] - Callback that will be invoked when
         *      all of the requested items are available.
         * @returns {*}
         *
         * @example Requesting item by it's name.
         *      var obj = {index: 'test', sample: true};
         *
         *      registry.set('first', obj);
         *      registry.get('first') === obj;
         *      => true
         *
         * @example Requesting item with a specific properties.
         *      registry.get('sample = 1, index = test') === obj;
         *      => true
         *      registry.get('sample = 0, index = foo') === obj;
         *      => false
         *
         * @example Declaring search criteria as an object.
         *      registry.get({sample: true}) === obj;
         *      => true;
         *
         * @example Providing custom search handler.
         *      registry.get(function (item) { return item.sample === true; }) === obj;
         *      => true
         *
         * @example Sample asynchronous request declaration.
         *      registry.get('index = test', function (item) {});
         *
         * @example Requesting multiple elements.
         *      registry.set('second', {index: 'test2'});
         *      registry.get(['first', 'second'], function (first, second) {});
         */
        get: function (query, callback) {
            if (typeof callback !== 'function') {
                return find(getItems(this), query);
            }

            this._addRequest(query, callback);
        },

        /**
         * Sets provided item to the registry.
         *
         * @param {String} id - Item's identifier.
         * @param {*} item - Item's data.
         * returns {Registry} Chainable.
         */
        set: function (id, item) {
            getItems(this)[id] = item;

            this._updateRequests();

            return this;
        },

        /**
         * Removes specified item from registry.
         * Note that search query is not applicable.
         *
         * @param {String} id - Item's identifier.
         * @returns {Registry} Chainable.
         */
        remove: function (id) {
            delete getItems(this)[id];

            return this;
        },

        /**
         * Retrieves a collection of elements that match
         * provided search criteria.
         *
         * @param {(Object|String|Function)} query - Search query.
         *      See 'get' method for the syntax examples.
         * @returns {Array} Found elements.
         */
        filter: function (query) {
            return find(getItems(this), query, true);
        },

        /**
         * Checks that at least one element in collection
         * matches provided search criteria.
         *
         * @param {(Object|String|Function)} query - Search query.
         *      See 'get' method for the syntax examples.
         * @returns {Boolean}
         */
        has: function (query) {
            return !!this.get(query);
        },

        /**
         * Checks that registry contains a provided item.
         *
         * @param {*} item - Item to be checked.
         * @returns {Boolean}
         */
        contains: function (item) {
            return _.contains(getItems(this), item);
        },

        /**
         * Extracts identifier of an item if it's present in registry.
         *
         * @param {*} item - Item whose identifier will be extracted.
         * @returns {String|Undefined}
         */
        indexOf: function (item) {
            return _.findKey(getItems(this), function (elem) {
                return item === elem;
            });
        },

        /**
         * Same as a 'get' method except that it returns
         * a promise object instead of invoking provided callback.
         *
         * @param {(String|Function|Object|Array)} query - Search query.
         *      See 'get' method for the syntax examples.
         * @returns {jQueryPromise}
         */
        promise: function (query) {
            var defer    = $.Deferred(),
                callback = defer.resolve.bind(defer);

            this.get(query, callback);

            return defer.promise();
        },

        /**
         * Creates a wrapper function over the provided search query
         * in order to provide somehow more convenient access to the
         * registry's items.
         *
         * @param {(String|Object|Function)} query - Search criteria.
         *      See 'get' method for the syntax examples.
         * @returns {Function}
         *
         * @example Comparison with a 'get' method on retrieving items.
         *      var module = registry.async('name');
         *
         *      module();
         *      => registry.get('name');
         *
         * @example Asynchronous request.
         *      module(function (component) {});
         *      => registry.get('name', function (component) {});
         *
         * @example Requesting item and invoking it's method with specified parameters.
         *      module('trigger', true);
         *      => registry.get('name', function (component) {
         *          component.trigger(true);
         *      });
         */
        async: function (query) {
            return async.bind(null, query, this);
        },

        /**
         * Creates new instance of a Registry.
         *
         * @returns {Registry} New instance.
         */
        create: function () {
            return new Registry;
        },

        /**
         * Adds new request to the queue or resolves it immediately
         * if all of the required items are available.
         *
         * @private
         * @param {(Object|String|Function|Array)} queries - Search criteria.
         *      See 'get' method for the syntax examples.
         * @param {Function} callback - Callback that will be invoked when
         *      all of the requested items are available.
         * @returns {Registry}
         */
        _addRequest: function (queries, callback) {
            var request;

            if (!Array.isArray(queries)) {
                queries = queries ? [queries] : [];
            }

            request = {
                queries: queries.map(explode),
                callback: callback
            };

            this._canResolve(request) ?
                this._resolveRequest(request) :
                getRequests(this).push(request);

            return this;
        },

        /**
         * Updates requests list resolving applicable items.
         *
         * @private
         * @returns {Registry} Chainable.
         */
        _updateRequests: function () {
            getRequests(this)
                .filter(this._canResolve, this)
                .forEach(this._resolveRequest, this);

            return this;
        },

        /**
         * Resolves provided request invoking it's callback
         * with items specified in query parameters.
         *
         * @private
         * @param {Object} request - Request object.
         * @returns {Registry} Chainable.
         */
        _resolveRequest: function (request) {
            var requests = getRequests(this),
                items    = request.queries.map(this.get, this),
                index    = requests.indexOf(request);

            request.callback.apply(null, items);

            if (~index) {
                requests.splice(index, 1);
            }

            return this;
        },

        /**
         * Checks if provided request can be resolved.
         *
         * @private
         * @param {Object} request - Request object.
         * @returns {Boolean}
         */
        _canResolve: function (request) {
            var queries = request.queries;

            return queries.every(this.has, this);
        }
    };

    return new Registry;
});

define("Magento_Ui/js/lib/registry/registry", function(){});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausSe/js/mixins/lib/core/element/element', ['uiRegistry'], function (registry) {
    'use strict';

    return function (uiElement) {
        return uiElement.extend({
            useSource: function (cb) {
                if (!this.provider) {return null}

                if (this.source) {
                    cb(this.source);
                    return;
                }

                registry.get(this.provider, cb);
            },
        });
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('Vaimo_BauhausSe/js/mixins/modal', [
    'jquery',
    'jquery/ui'
], function ($) {'use strict';
return function (originalWidget) {
const prototype = originalWidget.prototype;
let activePopup = undefined;
function closeOther(context) {
    if (activePopup) {
        const localPopup = activePopup;
        localPopup.canPopupBeClosed = true;
        localPopup.closeModal();
        localPopup._close();/*skipping transition events (instead of messing with transitionEvent option)*/
    }

    activePopup = context;
}

$.widget(prototype.namespace + '.' + prototype.widgetName, $[prototype.namespace][prototype.widgetName], {
    options: {
        canPopupBeClosedTimeoutTime: 100
    },

    /**
     * @inheritDoc
     */
    _close: function () {
        this._super();
        $(document).trigger('closeModal', this.modal);
        this.element.trigger('closeModalOnElement', this.modal);
    },

    /**
     * @inheritDoc
     */
    openModal: function () {
        if (this.options.isOpen) {
            this.canPopupBeClosed = true;
        } else {
            closeOther(this);
            this.canPopupBeClosed = false;
            window.clearTimeout(this.canPopupBeClosedTimeout);
            this.canPopupBeClosedTimeout = window.setTimeout((function () {
                this.canPopupBeClosed = true;
            }).bind(this), this.options.canPopupBeClosedTimeoutTime);
        }
        const chain = this._super();
        $(document).trigger('afterOpenModal', this.modal);
        return chain;
    },

    /**
     * @inheritDoc
     */
    closeModal: function () {
        if (this.canPopupBeClosed) {
            activePopup = undefined;
            return this._super();
        }

        return this.element;
    },

    /**
     * @inheritDoc
     */
    _destroyOverlay: function () {
        if (this.overlay) {
            return this._super();
        }

        return undefined;
    }
});

return $[prototype.namespace][prototype.widgetName];
}});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('Vaimo_BauhausSe/js/mixins/page-cache-mixin', [
    'jquery',
    'uiRegistry',
    'jquery/ui'
], function ($, registry) {
    'use strict';

return function () {
    var widgetNameSplit = 'mage.formKey'.split('.');
    var prototype = {
        namespace: widgetNameSplit[0],
        widgetName: widgetNameSplit[1]
    };

    $.widget(prototype.namespace + '.' + prototype.widgetName, $[prototype.namespace][prototype.widgetName], {
        _create: function () {
            this._super.apply(this, arguments);

            registry.set('formKeyIsSet', $.mage.cookies.get('form_key'));
        },
    });

    return $[prototype.namespace][prototype.widgetName];
};
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/apply/scripts',[
    'underscore',
    'jquery'
], function (_, $) {
    'use strict';

    var scriptSelector = 'script[type="text/x-magento-init"]',
        dataAttr = 'data-mage-init',
        virtuals = [];

    /**
     * Adds components to the virtual list.
     *
     * @param {Object} components
     */
    function addVirtual(components) {
        virtuals.push({
            el: false,
            data: components
        });
    }

    /**
     * Merges provided data with a current data
     * of a elements' "data-mage-init" attribute.
     *
     * @param {Object} components - Object with components and theirs configuration.
     * @param {HTMLElement} elem - Element whose data should be modified.
     */
    function setData(components, elem) {
        var data = elem.getAttribute(dataAttr);

        data = data ? JSON.parse(data) : {};
        _.each(components, function (obj, key) {
            if (_.has(obj, 'mixins')) {
                data[key] = data[key] || {};
                data[key].mixins = data[key].mixins || [];
                data[key].mixins = data[key].mixins.concat(obj.mixins);
                delete obj.mixins;
            }
        });

        data = $.extend(true, data, components);
        data = JSON.stringify(data);
        elem.setAttribute(dataAttr, data);
    }

    /**
     * Search for the elements by privded selector and extends theirs data.
     *
     * @param {Object} components - Object with components and theirs configuration.
     * @param {String} selector - Selector for the elements.
     */
    function processElems(components, selector) {
        var elems,
            iterator;

        if (selector === '*') {
            addVirtual(components);

            return;
        }

        elems = document.querySelectorAll(selector);
        iterator = setData.bind(null, components);

        _.toArray(elems).forEach(iterator);
    }

    /**
     * Parses content of a provided script node.
     * Note: node will be removed from DOM.
     *
     * @param {HTMLScriptElement} node - Node to be processed.
     * @returns {Object}
     */
    function getNodeData(node) {
        var data = node.textContent;

        node.parentNode.removeChild(node);

        return JSON.parse(data);
    }

    /**
     * Parses 'script' tags with a custom type attribute and moves it's data
     * to a 'data-mage-init' attribute of an element found by provided selector.
     * Note: All found script nodes will be removed from DOM.
     *
     * @returns {Array} An array of components not assigned to the specific element.
     *
     * @example Sample declaration.
     *      <script type="text/x-magento-init">
     *          {
     *              "body": {
     *                  "path/to/component": {"foo": "bar"}
     *              }
     *          }
     *      </script>
     *
     * @example Providing data without selector.
     *      {
     *          "*": {
     *              "path/to/component": {"bar": "baz"}
     *          }
     *      }
     */
    return function () {
        var nodes = document.querySelectorAll(scriptSelector);

        _.toArray(nodes)
            .map(getNodeData)
            .forEach(function (item) {
                _.each(item, processElems);
            });

        return virtuals.splice(0, virtuals.length);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/apply/main',[
    'underscore',
    'jquery',
    './scripts'
], function (_, $, processScripts) {
    'use strict';

    var dataAttr = 'data-mage-init',
        nodeSelector = '[' + dataAttr + ']';

    /**
     * Initializes components assigned to a specified element via data-* attribute.
     *
     * @param {HTMLElement} el - Element to initialize components with.
     * @param {Object|String} config - Initial components' config.
     * @param {String} component - Components' path.
     */
    function init(el, config, component) {
        require([component], function (fn) {
            var $el;

            try {if (typeof fn === 'object' && typeof fn[component] === 'function') {
                fn = fn[component].bind(fn);
            }

            if (_.isFunction(fn)) {
                fn = fn.bind(null, config, el);
            } else {
                $el = $(el);

                if ($el[component]) {
                    // eslint-disable-next-line jquery-no-bind-unbind
                    fn = $el[component].bind($el, config);
                }
            }}catch(e){console.error(e)}
            // Init module in separate task to prevent blocking main thread.
            setTimeout(typeof fn === 'function' ? fn : (console.warn(`No component executable found for "${component}"`),a=>a));
        }, function (error) {
            if ('console' in window && typeof window.console.error === 'function') {
                console.error(error);
            }

            return true;
        });
    }

    /**
     * Parses elements 'data-mage-init' attribute as a valid JSON data.
     * Note: data-mage-init attribute will be removed.
     *
     * @param {HTMLElement} el - Element whose attribute should be parsed.
     * @returns {Object}
     */
    function getData(el) {
        var data = el.getAttribute(dataAttr);

        el.removeAttribute(dataAttr);

        return {
            el: el,
            data: JSON.parse(data)
        };
    }

    return {
        /**
         * Initializes components assigned to HTML elements via [data-mage-init].
         *
         * @example Sample 'data-mage-init' declaration.
         *      data-mage-init='{"path/to/component": {"foo": "bar"}}'
         */
        apply: function (context) {
            var virtuals = processScripts(!context ? document : context),
                nodes = document.querySelectorAll(nodeSelector);

            _.toArray(nodes)
                .map(getData)
                .concat(virtuals)
                .forEach(function (itemContainer) {
                    var element = itemContainer.el;

                    _.each(itemContainer.data, function (obj, key) {
                            if (obj.mixins) {
                                require(obj.mixins, function () { //eslint-disable-line max-nested-callbacks
                                    var i, len;

                                    for (i = 0, len = arguments.length; i < len; i++) {
                                        $.extend(
                                            true,
                                            itemContainer.data[key],
                                            arguments[i](itemContainer.data[key], element)
                                        );
                                    }

                                    delete obj.mixins;
                                    init.call(null, element, obj, key);
                                });
                            } else {
                                init.call(null, element, obj, key);
                            }

                        }
                    );

                });
        },
        applyFor: init
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/mage',[
    'jquery',
    'mage/apply/main'
], function ($, mage) {
    'use strict';

    /**
     * Main namespace for Magento extensions
     * @type {Object}
     */
    $.mage = $.mage || {};

    /**
     * Plugin mage, initialize components on elements
     * @param {String} name - Components' path.
     * @param {Object} config - Components' config.
     * @returns {JQuery} Chainable.
     */
    $.fn.mage = function (name, config) {
        config = config || {};

        this.each(function (index, el) {
            mage.applyFor(el, config, name);
        });

        return this;
    };

    $.extend($.mage, {
        /**
         * Handle all components declared via data attribute
         * @return {Object} $.mage
         */
        init: function () {
            mage.apply();

            return this;
        },

        /**
         * Method handling redirects and page refresh
         * @param {String} url - redirect URL
         * @param {(undefined|String)} type - 'assign', 'reload', 'replace'
         * @param {(undefined|Number)} timeout - timeout in milliseconds before processing the redirect or reload
         * @param {(undefined|Boolean)} forced - true|false used for 'reload' only
         */
        redirect: function (url, type, timeout, forced) {
            var _redirect;

            forced  = !!forced;
            timeout = timeout || 0;
            type    = type || 'assign';

            /**
             * @private
             */
            _redirect = function () {
                window.location[type](type === 'reload' ? forced : url);
            };

            timeout ? setTimeout(_redirect, timeout) : _redirect();
        },

        /**
         * Checks if provided string is a valid selector.
         * @param {String} selector - Selector to check.
         * @returns {Boolean}
         */
        isValidSelector: function (selector) {
            try {
                document.querySelector(selector);

                return true;
            } catch (e) {
                return false;
            }
        }
    });

    /**
     * Init components inside of dynamically updated elements
     */
    $(document).on('contentUpdated', 'body', function () {
        if (mage) {
            mage.apply();
        }
    });

    return $.mage;
});

if (typeof window === 'undefined') {
    /*TODO: remove after Magento will understand they've commited load (text.js replaces requirejs' text.js) file that cannot work in no broswers environment*/



    /**
     * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
     * Available via the MIT or new BSD license.
     * see: http://github.com/requirejs/text for details
     */
    /*jslint regexp: true */
    /*global require, XMLHttpRequest, ActiveXObject,
     define, window, process, Packages,
     java, location, Components, FileUtils */

    define('text', ['module'], function (module) {
        'use strict';

        var text, fs, Cc, Ci, xpcIsWindows,
            progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
            xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
            bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
            hasLocation = typeof location !== 'undefined' && location.href,
            defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
            defaultHostName = hasLocation && location.hostname,
            defaultPort = hasLocation && (location.port || undefined),
            buildMap = {},
            masterConfig = (module.config && module.config()) || {};

        text = {
            version: '2.0.12',

            strip: function (content) {
                //Strips <?xml ...?> declarations so that external SVG and XML
                //documents can be added to a document without worry. Also, if the string
                //is an HTML document, only the part inside the body tag is returned.
                if (content) {
                    content = content.replace(xmlRegExp, "");
                    var matches = content.match(bodyRegExp);
                    if (matches) {
                        content = matches[1];
                    }
                } else {
                    content = "";
                }
                return content;
            },

            jsEscape: function (content) {
                return content.replace(/(['\\])/g, '\\$1')
                    .replace(/[\f]/g, "\\f")
                    .replace(/[\b]/g, "\\b")
                    .replace(/[\n]/g, "\\n")
                    .replace(/[\t]/g, "\\t")
                    .replace(/[\r]/g, "\\r")
                    .replace(/[\u2028]/g, "\\u2028")
                    .replace(/[\u2029]/g, "\\u2029");
            },

            createXhr: masterConfig.createXhr || function () {
                //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
                var xhr, i, progId;
                if (typeof XMLHttpRequest !== "undefined") {
                    return new XMLHttpRequest();
                } else if (typeof ActiveXObject !== "undefined") {
                    for (i = 0; i < 3; i += 1) {
                        progId = progIds[i];
                        try {
                            xhr = new ActiveXObject(progId);
                        } catch (e) {}

                        if (xhr) {
                            progIds = [progId];  // so faster next time
                            break;
                        }
                    }
                }

                return xhr;
            },

            /**
             * Parses a resource name into its component parts. Resource names
             * look like: module/name.ext!strip, where the !strip part is
             * optional.
             * @param {String} name the resource name
             * @returns {Object} with properties "moduleName", "ext" and "strip"
             * where strip is a boolean.
             */
            parseName: function (name) {
                var modName, ext, temp,
                    strip = false,
                    index = name.indexOf("."),
                    isRelative = name.indexOf('./') === 0 ||
                        name.indexOf('../') === 0;

                if (index !== -1 && (!isRelative || index > 1)) {
                    modName = name.substring(0, index);
                    ext = name.substring(index + 1, name.length);
                } else {
                    modName = name;
                }

                temp = ext || modName;
                index = temp.indexOf("!");
                if (index !== -1) {
                    //Pull off the strip arg.
                    strip = temp.substring(index + 1) === "strip";
                    temp = temp.substring(0, index);
                    if (ext) {
                        ext = temp;
                    } else {
                        modName = temp;
                    }
                }

                return {
                    moduleName: modName,
                    ext: ext,
                    strip: strip
                };
            },

            xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,

            /**
             * Is an URL on another domain. Only works for browser use, returns
             * false in non-browser environments. Only used to know if an
             * optimized .js version of a text resource should be loaded
             * instead.
             * @param {String} url
             * @returns Boolean
             */
            useXhr: function (url, protocol, hostname, port) {
                var uProtocol, uHostName, uPort,
                    match = text.xdRegExp.exec(url);
                if (!match) {
                    return true;
                }
                uProtocol = match[2];
                uHostName = match[3];

                uHostName = uHostName.split(':');
                uPort = uHostName[1];
                uHostName = uHostName[0];

                return (!uProtocol || uProtocol === protocol) &&
                    (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
                    ((!uPort && !uHostName) || uPort === port);
            },

            finishLoad: function (name, strip, content, onLoad) {
                content = strip ? text.strip(content) : content;
                if (masterConfig.isBuild) {
                    buildMap[name] = content;
                }
                onLoad(content);
            },

            load: function (name, req, onLoad, config) {
                //Name has format: some.module.filext!strip
                //The strip part is optional.
                //if strip is present, then that means only get the string contents
                //inside a body tag in an HTML string. For XML/SVG content it means
                //removing the <?xml ...?> declarations so the content can be inserted
                //into the current doc without problems.

                // Do not bother with the work if a build and text will
                // not be inlined.
                if (config && config.isBuild && !config.inlineText) {
                    onLoad();
                    return;
                }

                masterConfig.isBuild = config && config.isBuild;

                var parsed = text.parseName(name),
                    nonStripName = parsed.moduleName +
                        (parsed.ext ? '.' + parsed.ext : ''),
                    url = req.toUrl(nonStripName),
                    useXhr = (masterConfig.useXhr) ||
                        text.useXhr;

                // Do not load if it is an empty: url
                if (url.indexOf('empty:') === 0) {
                    onLoad();
                    return;
                }

                //Load the text. Use XHR if possible and in a browser.
                if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
                    text.get(url, function (content) {
                        text.finishLoad(name, parsed.strip, content, onLoad);
                    }, function (err) {
                        if (onLoad.error) {
                            onLoad.error(err);
                        }
                    });
                } else {
                    //Need to fetch the resource across domains. Assume
                    //the resource has been optimized into a JS module. Fetch
                    //by the module name + extension, but do not include the
                    //!strip part to avoid file system issues.
                    req([nonStripName], function (content) {
                        text.finishLoad(parsed.moduleName + '.' + parsed.ext,
                            parsed.strip, content, onLoad);
                    });
                }
            },

            write: function (pluginName, moduleName, write, config) {
                if (buildMap.hasOwnProperty(moduleName)) {
                    var content = text.jsEscape(buildMap[moduleName]);
                    write.asModule(pluginName + "!" + moduleName,
                        "define(function () { return '" +
                        content +
                        "';});\n");
                }
            },

            writeFile: function (pluginName, moduleName, req, write, config) {
                var parsed = text.parseName(moduleName),
                    extPart = parsed.ext ? '.' + parsed.ext : '',
                    nonStripName = parsed.moduleName + extPart,
                    //Use a '.js' file name so that it indicates it is a
                    //script that can be loaded across domains.
                    fileName = req.toUrl(parsed.moduleName + extPart) + '.js';

                //Leverage own load() method to load plugin value, but only
                //write out values that do not have the strip argument,
                //to avoid any potential issues with ! in file names.
                text.load(nonStripName, req, function (value) {
                    //Use own write() method to construct full module value.
                    //But need to create shell that translates writeFile's
                    //write() to the right interface.
                    var textWrite = function (contents) {
                        return write(fileName, contents);
                    };
                    textWrite.asModule = function (moduleName, contents) {
                        return write.asModule(moduleName, fileName, contents);
                    };

                    text.write(pluginName, nonStripName, textWrite, config);
                }, config);
            }
        };

        if (masterConfig.env === 'node' || (!masterConfig.env &&
            typeof process !== "undefined" &&
            process.versions &&
            !!process.versions.node &&
            !process.versions['node-webkit'])) {
            //Using special require.nodeRequire, something added by r.js.
            fs = require.nodeRequire('fs');

            text.get = function (url, callback, errback) {
                try {
                    var file = fs.readFileSync(url, 'utf8');
                    //Remove BOM (Byte Mark Order) from utf8 files if it is there.
                    if (file.indexOf('\uFEFF') === 0) {
                        file = file.substring(1);
                    }
                    callback(file);
                } catch (e) {
                    if (errback) {
                        errback(e);
                    }
                }
            };
        } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
            text.createXhr())) {
            text.get = function (url, callback, errback, headers) {
                var xhr = text.createXhr(), header;
                xhr.open('GET', url, true);

                //Allow plugins direct access to xhr headers
                if (headers) {
                    for (header in headers) {
                        if (headers.hasOwnProperty(header)) {
                            xhr.setRequestHeader(header.toLowerCase(), headers[header]);
                        }
                    }
                }

                //Allow overrides specified in config
                if (masterConfig.onXhr) {
                    masterConfig.onXhr(xhr, url);
                }

                xhr.onreadystatechange = function (evt) {
                    var status, err;
                    //Do not explicitly handle errors, those should be
                    //visible via console output in the browser.
                    if (xhr.readyState === 4) {
                        status = xhr.status || 0;
                        if (status > 399 && status < 600) {
                            //An http 4xx or 5xx error. Signal an error.
                            err = new Error(url + ' HTTP status: ' + status);
                            err.xhr = xhr;
                            if (errback) {
                                errback(err);
                            }
                        } else {
                            callback(xhr.responseText);
                        }

                        if (masterConfig.onXhrComplete) {
                            masterConfig.onXhrComplete(xhr, url);
                        }
                    }
                };
                xhr.send(null);
            };
        } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
            typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
            //Why Java, why is this so awkward?
            text.get = function (url, callback) {
                var stringBuffer, line,
                    encoding = "utf-8",
                    file = new java.io.File(url),
                    lineSeparator = java.lang.System.getProperty("line.separator"),
                    input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
                    content = '';
                try {
                    stringBuffer = new java.lang.StringBuffer();
                    line = input.readLine();

                    // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
                    // http://www.unicode.org/faq/utf_bom.html

                    // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
                    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
                    if (line && line.length() && line.charAt(0) === 0xfeff) {
                        // Eat the BOM, since we've already found the encoding on this file,
                        // and we plan to concatenating this buffer with others; the BOM should
                        // only appear at the top of a file.
                        line = line.substring(1);
                    }

                    if (line !== null) {
                        stringBuffer.append(line);
                    }

                    while ((line = input.readLine()) !== null) {
                        stringBuffer.append(lineSeparator);
                        stringBuffer.append(line);
                    }
                    //Make sure we return a JavaScript string and not a Java string.
                    content = String(stringBuffer.toString()); //String
                } finally {
                    input.close();
                }
                callback(content);
            };
        } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
            typeof Components !== 'undefined' && Components.classes &&
            Components.interfaces)) {
            //Avert your gaze!
            Cc = Components.classes;
            Ci = Components.interfaces;
            Components.utils['import']('resource://gre/modules/FileUtils.jsm');
            xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);

            text.get = function (url, callback) {
                var inStream, convertStream, fileObj,
                    readData = {};

                if (xpcIsWindows) {
                    url = url.replace(/\//g, '\\');
                }

                fileObj = new FileUtils.File(url);

                //XPCOM, you so crazy
                try {
                    inStream = Cc['@mozilla.org/network/file-input-stream;1']
                        .createInstance(Ci.nsIFileInputStream);
                    inStream.init(fileObj, 1, 0, false);

                    convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
                        .createInstance(Ci.nsIConverterInputStream);
                    convertStream.init(inStream, "utf-8", inStream.available(),
                        Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);

                    convertStream.readString(inStream.available(), readData);
                    convertStream.close();
                    inStream.close();
                    callback(readData.value);
                } catch (e) {
                    throw new Error((fileObj && fileObj.path || '') + ': ' + e);
                }
            };
        }
        return text;
    });


} else {





    /**
     * Copyright © Vaimo Group. All rights reserved.
     * See LICENSE_VAIMO.txt for license details.
     */
    /* inspired by http://github.com/requirejs/text */
    /*global XMLHttpRequest, XDomainRequest */

    define('text', ['module'], function (module) {
        'use strict';

        var xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
            bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
            stripReg = /!strip$/i,
            defaultConfig = module.config && module.config() || {};

        /**
         * Strips <?xml ...?> declarations so that external SVG and XML documents can be
         * added to a document without worry.
         * Also, if the string is an HTML document, only the part inside the body tag is returned.
         *
         * @param {String} external
         * @returns {String}
         */
        function stripContent(external) {
            var matches;

            if (!external) {
                return '';
            }

            matches = external.match(bodyRegExp);
            external = matches ?
                matches[1] :
                external.replace(xmlRegExp, '');

            return external;
        }

        /**
         * Checks that url match current location
         *
         * @param {String} url
         * @returns {Boolean}
         */
        function sameDomain(url) {
            var uProtocol, uHostName, uPort,
                xdRegExp = /^([\w:]+)?\/\/([^\/\\]+)/i,
                location = window.location,
                match = xdRegExp.exec(url);

            if (!match) {
                return true;
            }
            uProtocol = match[1];
            uHostName = match[2];

            uHostName = uHostName.split(':');
            uPort = uHostName[1] || '';
            uHostName = uHostName[0];

            return (!uProtocol || uProtocol === location.protocol) &&
                (!uHostName || uHostName.toLowerCase() === location.hostname.toLowerCase()) &&
                (!uPort && !uHostName || uPort === location.port);
        }

        /**
         * @returns {XMLHttpRequest|XDomainRequest|null}
         */
        function createRequest(url) {
            var xhr = new XMLHttpRequest();

            if (!sameDomain(url) && typeof XDomainRequest !== 'undefined') {
                xhr = new XDomainRequest();
            }

            return xhr;
        }

        /**
         * XHR requester. Returns value to callback.
         *
         * @param {String} url
         * @param {Function} callback
         * @param {Function} fail
         * @param {Object} headers
         */
        function getContent(url, callback, fail, headers) {
            var xhr = createRequest(url),
                header;

            xhr.open('GET', url);

            /*eslint-disable max-depth */
            if ('setRequestHeader' in xhr && headers) {
                for (header in headers) {
                    if (headers.hasOwnProperty(header)) {
                        xhr.setRequestHeader(header.toLowerCase(), headers[header]);
                    }
                }
            }

            /**
             * @inheritdoc
             */
            xhr.onreadystatechange = function () {
                var status, err;

                //Do not explicitly handle errors, those should be
                //visible via console output in the browser.
                if (xhr.readyState === 4) {
                    status = xhr.status || 0;

                    if (status > 399 && status < 600) {
                        //An http 4xx or 5xx error. Signal an error.
                        err = new Error(url + ' HTTP status: ' + status);
                        err.xhr = xhr;

                        if (fail) {
                            fail(err);
                        }
                    } else {
                        callback(xhr.responseText);

                        if (defaultConfig.onXhrComplete) {
                            defaultConfig.onXhrComplete(xhr, url);
                        }
                    }
                }
            };

            /*eslint-enable max-depth */

            if (defaultConfig.onXhr) {
                defaultConfig.onXhr(xhr, url);
            }

            xhr.send();
        }

        /**
         * Main method used by RequireJs.
         *
         * @param {String} name - has format: some.module.filext!strip
         * @param {Function} req
         * @param {Function|undefined} onLoad
         */
        function loadContent(name, req, onLoad) {

            var toStrip = stripReg.test(name),
                url = req.toUrl(name.replace(stripReg, '')),
                headers = defaultConfig.headers;

            getContent(url, function (content) {
                content = toStrip ? stripContent(content) : content;
                onLoad(content);
            }, onLoad.error, headers);
        }

        return {
            load: loadContent,
            get: getContent
        };
    });
}
;

define('text!js-translation.json',[],function () { return '{" Example: ":" Exempel: "," is required.":" \\u00e4r obligatorisk.","${ $.visible } out of ${ $.total } visible":"${ $.visible } av ${ $.total } synliga","%1 items":"%1 artiklar","%price \\/unit":"%price \\/st","%qty items in stock":"%qty st i weblager","* Required Fields":"* Obligatoriska f\\u00e4lt","-- Please Select Billing Agreement--":"-- Var god v\\u00e4lj faktureringsavtal --","<strong>%s<\\/strong> was not uploaded<br\\/>":"<strong>%s<\\/strong> laddades inte upp.<br\\/>","A positive or negative non-decimal number please":"Ett positivt eller negativt tal, ej decimaltal tack.","Account Number":"Kontonummer","Action":"\\u00c5tg\\u00e4rd","Actions":"\\u00c5tg\\u00e4rder","Actual Price":"Faktiskt pris","Add":"L\\u00e4gg till","Add New User":"L\\u00e4gg till ny anv\\u00e4ndare","Add all to favorites":"G\\u00f6r alla till favoriter","Add to Cart":"L\\u00e4gg i varukorg","Add to Wish List":"L\\u00e4gg till i favoriter","Added":"Tillagd","Added to Wish List":"Tillagd i favoriter","Added to the Shopping Cart":"Tillagt i varukorgen","Adding...":"L\\u00e4gg i varukorg","Address":"Adress","Address and city":"Adress och postort","An error occurred":"Ett fel uppstod","Apply":"Anv\\u00e4nd","Apply Discount":"Till\\u00e4mpa rabatt","Apply Discount Code":"Till\\u00e4mpa rabattkod","Are you sure you want to delete this address?":"\\u00c4r du s\\u00e4ker p\\u00e5 att du vill ta bort denna adress?","Are you sure you would like to remove this item from the shopping cart?":"\\u00c4r du s\\u00e4ker p\\u00e5 att du vill ta bort denna produkt fr\\u00e5n varukorgen?","Assign":"Tilldela","Attention":"Meddelande","Attention: Captcha is case sensitive.":"Notera: Captcha \\u00e4r skiftesl\\u00e4gesk\\u00e4nsligt.","Availability":"Tillg\\u00e4nglighet","Available bonus points:":"Tillg\\u00e4ngliga bonuspo\\u00e4ng:","BAUHAUS Company Service":"BAUHAUS F\\u00f6retagsservice","Back":"Tillbaka","Backup options":"Backup-val","BankID did not open?":"\\u00d6ppnades inte BankID?","Bauhaus in store sales system":"BAUHAUS varuhusf\\u00f6rs\\u00e4ljning","Billing & shipping address":"Fakturerings- och leveransadress","Billing Address":"Faktureringsadress","Book advice":"Boka r\\u00e5dgivning","Book_send_request_e_s_p":"Boka","Brands":"Varum\\u00e4rken","Browse to find or drag image here":"Bl\\u00e4ddra eller dra bild hit","Buy one product and get another one with a discount.":"K\\u00f6p en produkt och f\\u00e5 en annan till en rabatt","Campaign":"Kampanj","Cancel":"Avbryt","Cancel coupon":"Avbryt kupong","Cannot deliver to box address!":"Kan inte leverera till boxadress!","Card Number":"Kortnummer","Card Verification Number":"Verifieringsnummer f\\u00f6r Bankkort","Card Verification Number Visual Reference":"Visuell referens f\\u00f6r verifieringsnummer f\\u00f6r bankkort","Card type does not match credit card number.":"Korttyp matchar inte kreditkortsnummer.","Carrier Title":"Expedit\\u00f6r titel","Cart Subtotal":"Orderv\\u00e4rde","Categories":"Kategorier","Change":"\\u00c4ndra","Change pickup location":"\\u00c4ndra postombud","Changes have been made to this section that have not been saved.":"\\u00c4ndringar har gjorts i den h\\u00e4r sektionen, men inte sparats.","Choose a selection...":"V\\u00e4lj ett alternativ...","Choose service point":"V\\u00e4lj postombud","Choose store":"V\\u00e4lj Varuhus","City":"Stad","City, Country":"Ort, Land","Clear All":"Rensa alla","Clear all":"Rensa alla filter","Click for price":"Klicka f\\u00f6r pris","Close":"St\\u00e4ng","Closed":"St\\u00e4ngd","Company":"F\\u00f6retag","Company Name":"F\\u00f6retagsnamn (juridiskt):","Confirm":"Bekr\\u00e4fta","Confirm Email Address":"Bekr\\u00e4fta e-postadress","Contact details":"Kontaktuppgifter","Continue":"Forts\\u00e4tt","Continue to PayPal":"Forts\\u00e4tt till PayPal","Continue with BankID":"Forts\\u00e4tt med BankID","Could not add your %s code.":"Ditt %s kunde tyv\\u00e4rr inte l\\u00e4ggas till.","Create an Account":"Skapa ett konto","Creating an account has many benefits:":"Att registrera sig som kund hos oss har m\\u00e5nga f\\u00f6rdelar","Credit Card Information":"Bankkortsinformation","Credit Card Number":"Bankkortsnummer","Credit Terms and Conditions":"Kreditvillkor","Credit card number does not match credit card type.":"Kreditkortsnummer matchar inte typ av kreditkort.","Custom":"Anpassad","Customer Card Number":"Kundkortsnummer","Day":"Dag","Default":"F\\u00f6rvalt","Default View":"F\\u00f6rvald vy","Delete":"Ta bort","Delete image":"Ta bort bild","Deliver to:":"Leverans till:","Delivery Terms and Conditions":"Leveransvillkor","Delivery to another address":"Leverera till en annan adress","Delivery to project address":"Leverera till projektadress","Description":"Beskrivning","Deselect All":"Avmarkera alla","Deselect All on This Page":"Avmarkera alla p\\u00e5 den h\\u00e4r sidan","Discard selections":"Inget alternativ","Discount":"Rabatt","Do not have Mobile BankID?":"Har du inte mobilt BankID?","Done":"Klart","Earliest %s":"Tidigast %s","Edit":"\\u00c4ndra","Edit User":"\\u00c4ndra anv\\u00e4ndare","Edit Your Cart":"\\u00c4ndra i din varukorg","Edit project":"Redigera projekt","Email Address":"E-postadress","Empty Value.":"Tomt v\\u00e4rde.","Energy label":"Energietikett","Enter discount code":"Ange rabattkod","Enter your destination to get a shipping estimate.":"V\\u00e4lj destination f\\u00f6r att f\\u00e5 en uppskattad fraktkostnad.","Error":"Fel uppstod","Estimate Shipping and Tax":"Ber\\u00e4kna frakt och moms","Estimate Tax":"Ber\\u00e4kna moms","Estimated Total":"Summa","Excl. Tax":"Exkl. moms","Expiration Date":"Utg\\u00e5ngsdatum","Export":"Exportera","Field ":"F\\u00e4lt ","File you are trying to upload exceeds maximum file size limit.":"Storleken p\\u00e5 filen du f\\u00f6rs\\u00f6ker ladda upp \\u00e4r st\\u00f6rre \\u00e4n gr\\u00e4nsen.","File you are trying to upload exceeds maximum of %s.":"Storleken p\\u00e5 filen du f\\u00f6rs\\u00f6ker ladda upp \\u00e4r st\\u00f6rre \\u00e4n %s.","Fill in the form and we will call you":"Fyll i formul\\u00e4ret s\\u00e5 ringer vi upp dig","Fill in the form and we will contact you":"Fyll i formul\\u00e4ret s\\u00e5 kontakta vi upp dig","Filters":"Filtrera","Final Price":"Slutpris","First Name":"F\\u00f6rnamn","Forgot Your Password?":"Gl\\u00f6mt ditt l\\u00f6senord?","Forgot an Item?":"Gl\\u00f6mt en produkt?","Friday":"Fredag","From %1":"Fr\\u00e5n %1","From:":"Fr\\u00e5n:","Full Name":"Fullst\\u00e4ndigt namn","Gift Message (optional)":"Presentmeddelande (valfritt)","Go to Checkout":"G\\u00e5 till kassan","Go to Details Page":"G\\u00e5 till detaljsidan","Go to Home Page":"G\\u00e5 till startsidan","Grand Total":"Att betala","Guest checkout is disabled.":"G\\u00e4stkassan \\u00e4r inaktiverad.","HTML tags are not allowed.":"HTML taggar \\u00e4r till\\u00e5tna","Help":"Hj\\u00e4lp","Holidays":"H\\u00f6gtider","Home":"Hem","If you believe it is the right one you can ignore this notice.":"Du kan ignorera detta meddelande om du \\u00e4r s\\u00e4ker p\\u00e5 att postkoden \\u00e4r korrekt.","In stock in:":"I lager i:","In stock online":"I lager online","Incl. Tax":"Inkl. moms","Incorrect Captcha":"Felaktig Captcha","Incorrect credit card expiration date.":"Felaktigt kreditkorts utg\\u00e5ngsdatum.","Insert Widget":"L\\u00e4gg till Widget","Invalid date":"Ogiltigt datum","Invalid format.":"Ogiltigt format.","Item in Cart":"Artikel i varukorg","Items in Cart":"Artiklar i varukorg","Keyword":"Nyckelord","Last Name":"Efternamn","Let us perform a quick verification":"L\\u00e5t oss g\\u00f6ra en snabb verifiering","Letters only please":"Endast bokst\\u00e4ver, tack","Letters or punctuation only please":"Endast bokst\\u00e4ver och skiljetecken, tack","Letters, numbers, spaces or underscores only please":"Endast bokst\\u00e4ver, siffror, mellanslag eller understreck, tack","Loading":"Laddar","Loading...":"Laddar...","Log out":"Logga ut","Login":"Logga in","Login or Register":"Logga in eller skapa konto","Make Check payable to:":"G\\u00f6r checken p\\u00e5:","Manual order session has been cleaned":"Manuell ordningssession har rensats","Max":"Maximalt","Medium":"Medel","Message":"Meddelande","Message:":"Meddelande:","Method %s does not exist on jQuery.decorate":"Metoden %s existerar inte p\\u00e5 jQuery.decorate","Min":"Minst","Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.":"V\\u00e4nligen ange ett starkt l\\u00f6senord som inneh\\u00e5ller b\\u00e5de sm\\u00e5 och stora bokst\\u00e4ver, siffror samt specialtecken.","Mobile Number":"Mobilnummer","Monday":"M\\u00e5ndag","Month":"M\\u00e5nad","Multiple choices":"Flera val","Multiple deliveries":"Flera leveranser","My Cart":"Varukorgen","My billing and shipping address are the same":"Min faktura- och leveransadress \\u00e4r desamma.","Name":"Namn","New Address":"Ny adress","New View":"Ny vy","New item":"Nyhet","Next":"N\\u00e4sta","No":"Nej","No Password":"Inget l\\u00f6senord","No Payment Methods":"Inga betals\\u00e4tt","No Payment method available.":"Inget betals\\u00e4tt tillg\\u00e4ngligt.","No filter for:":"Inget filter f\\u00f6r:","No records found.":"Inga poster har hittats.","No results for \\"%1\\", try again.":"Inga tr\\u00e4ffar p\\u00e5 \\"%1\\", f\\u00f6rs\\u00f6k igen.","No white space please":"Inga blanksteg, tack","Not yet calculated":"\\u00c4nnu ej ber\\u00e4knad","Not you?":"Inte du?","Number must start with {0}":"Numret m\\u00e5ste b\\u00f6rja med {0}","Off":"Av","Ok, close":"Ok, st\\u00e4ng","On":"P\\u00e5","Open BankID app on mobile device":"\\u00d6ppna BankID-appen p\\u00e5 din mobila enhet","Open BankID app on this device":"\\u00d6ppna BankID p\\u00e5 den h\\u00e4r enheten","Options":"Alternativ","Or":"Eller","Ord. Price:":"Ord. pris:","Order Summary":"Granska best\\u00e4llning","Order number":"Ert ordernummer","Other addresses montage service could be ordered for":"Annan adress som montaget \\u00f6nskas utf\\u00f6ras p\\u00e5","Other available goods with the same discount":"Varor med samma rabatt","Out of stock":"Ej i lager","Out of stock online":"Tillf\\u00e4lligt slut p\\u00e5 n\\u00e4tet","Outgoing":"Utg\\u00e5ende","Page":"Sida","Pages and guides":"Sidor & guider","Password":"L\\u00f6senord","Payment Information":"Information om betalning","Payment Method":"Betalningss\\u00e4tt","Person SSN":"Personnummer","Pick-up Terms and Conditions":"Villkor","Place Order":"L\\u00e4gg best\\u00e4llning","Please Select":"V\\u00e4nligen v\\u00e4lj","Please choose a payment method.":"V\\u00e4nligen v\\u00e4lj betalningsmetod.","Please enter %s characters.":"V\\u00e4nligen ange %s tecken.","Please enter 6 or more characters. Leading and trailing spaces will be ignored.":"V\\u00e4nligen ange 6 eller fler tecken. Mellanslag i b\\u00f6rjan och slutet ignoreras.","Please enter 7 or more characters, using both numeric and alphabetic.":"V\\u00e4nligen ange 7 eller fler tecken, b\\u00e5de numeriska och alfabetiska.","Please enter a correct date":"V\\u00e4nligen ange ett korrekt datum","Please enter a date between %min and %max.":"V\\u00e4nligen ange ett datum mellan %min och %max.","Please enter a date from the past.":"V\\u00e4nligen ange ett datum som har passerat.","Please enter a number 0 or greater in this field.":"V\\u00e4nligen ange en siffra 0 eller st\\u00f6rre i detta f\\u00e4lt.","Please enter a number greater than 0 in this field.":"V\\u00e4nligen ange ett tal som \\u00e4r st\\u00f6rre \\u00e4n 0 i detta f\\u00e4lt.","Please enter a valid $ amount. For example $100.00.":"V\\u00e4nligen ange ett giltigt belopp. Exempelvis 100.","Please enter a valid IP v4 address.":"V\\u00e4nligen ange en giltig IP v4-adress.","Please enter a valid IP v6 address.":"V\\u00e4nligen ange en giltig IP v6 address.","Please enter a valid URL Key (Ex: \\"example-page\\", \\"example-page.html\\" or \\"anotherlevel\\/example-page\\").":"V\\u00e4nligen ange en giltig URL-key (t.ex. \\"exempelsida\\", \\"exempelsida.html\\" eller \\"annansida\\/exempelsida\\").","Please enter a valid URL. For example http:\\/\\/www.example.com or www.example.com.":"V\\u00e4nligen ange en giltig url, t.ex. http:\\/\\/www.exempel.com or www.exempel.com.","Please enter a valid URL. Protocol is required (http:\\/\\/, https:\\/\\/ or ftp:\\/\\/).":"V\\u00e4nligen ange en giltig URL. Protokoll kr\\u00e4vs (http:\\/\\/, https:\\/\\/ eller ftp:\\/\\/).","Please enter a valid XML-identifier (Ex: something_1, block5, id-4).":"V\\u00e4nligen ange en giltig XML-identifierare (t.ex: something_1, block5, id-4).","Please enter a valid address":"V\\u00e4nligen ange en giltig adress","Please enter a valid credit card number.":"V\\u00e4nligen ange ett giltigt bankkortsnummer.","Please enter a valid credit card verification number.":"Ange ett giltigt kreditkort kontrollnummer.","Please enter a valid date.":"V\\u00e4nligen ange ett giltigt datum.","Please enter a valid day (1-%1).":"V\\u00e4nligen ange en giltig dag (1-%1).","Please enter a valid email address (Ex: johndoe@domain.com).":"V\\u00e4nligen ange en giltig e-postadress utan otill\\u00e5tna tecken som \\u00c5,\\u00c4,\\u00d6. Tex: namn@example.com","Please enter a valid email address.":"V\\u00e4nligen ange en giltig e-postadress.","Please enter a valid fax number (Ex: 123-456-7890).":"V\\u00e4nligen ange ett giltigt faxnummer (t.ex: 123-456-7890).","Please enter a valid mobile number, for example 0721111111":"V\\u00e4nligen ange ett giltigt mobilnummer, exempelvis 0721111111","Please enter a valid number in this field.":"V\\u00e4nligen ange ett giltigt nummer i detta f\\u00e4lt.","Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.":"V\\u00e4nligen ange ett giltigt telefonnummer, exempelvis 08-123 456.","Please enter a valid social security number (Ex: 123-45-6789).":"V\\u00e4nligen ange ett giltigt personnummer, exempelvis 010101-0106.","Please enter a valid time, between 00:00 am and 12:00 pm":"V\\u00e4nligen ange en giltig tid, mellan 00:00 fm och 12:00 em","Please enter a valid time, between 00:00 and 23:59":"V\\u00e4nligen ange en giltig tid, mellan 00:00 och 23:59","Please enter a valid value from list":"V\\u00e4nligen ange ett giltigt v\\u00e4rde fr\\u00e5n listan","Please enter a valid value, ex: 10,20,30":"V\\u00e4nligen ange ett giltigt v\\u00e4rde, t.ex. 10, 20, 30","Please enter a valid year (1900-%1).":"V\\u00e4nligen ange ett giltigt \\u00e5r (1900-%1).","Please enter a valid zip code (Ex: 90602 or 90602-1234).":"V\\u00e4nligen ange ett giltigt postnummer (t.ex: 12345 eller 123 45).","Please enter a valid zip code.":"V\\u00e4nligen ange ett giltigt postnummer.","Please enter a value greater than or equal to %s.":"V\\u00e4nligen ange ett v\\u00e4rde st\\u00f6rre \\u00e4n eller lika med %s.","Please enter a value greater than or equal to {0}.":"V\\u00e4nligen ange ett v\\u00e4rde st\\u00f6rre \\u00e4n eller lika med {0}.","Please enter a value less than or equal to %s.":"V\\u00e4nligen ange ett v\\u00e4rde mindre \\u00e4n eller lika med %s.","Please enter a value less than or equal to {0}.":"V\\u00e4nligen ange ett v\\u00e4rde mindre \\u00e4n eller lika med {0}.","Please enter at least {0} characters":"V\\u00e4nligen ange minst {0} tecken","Please enter at least {0} words.":"V\\u00e4nligen ange minst {0} ord.","Please enter between {0} and {1} words.":"V\\u00e4nligen ange mellan {0} och {1} ord.","Please enter issue number or start date for switch\\/solo card type.":"Ange ett l\\u00f6pnummer eller startdatum f\\u00f6r switch\\/solo korttyp.","Please enter less or equal than {0} characters.":"Numret ska vara max {0} tecken.","Please enter less or equal than {0} symbols.":"V\\u00e4nligen ange mindre \\u00e4n eller lika med {0} symboler.","Please enter more or equal than {0} characters.":"Numret ska vara minst {0} tecken.","Please enter more or equal than {0} symbols.":"V\\u00e4nligen ange st\\u00f6rre \\u00e4n eller lika med {0} symboler.","Please enter positive number in this field.":"V\\u00e4nligen ange positiva tal i detta f\\u00e4lt.","Please enter the same value again.":"V\\u00e4nligen se till att dina l\\u00f6senord \\u00f6verensst\\u00e4mmer.","Please enter {0} words or less.":"V\\u00e4nligen ange {0} ord eller f\\u00e4rre.","Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).":"V\\u00e4nligen ange en giltig CSS-l\\u00e4ngd (t.ex: 100px, 77pt, 20em, .5ex or 50%).","Please make sure your Email Addresses match.":"V\\u00e4nligen se till att dina E-postadresserna matchar","Please make sure your organisation ssn is correct.":"Kontrollera att ni har skrivit in ett korrekt organisationsnummer.","Please make sure your passwords match.":"V\\u00e4nligen se till att dina l\\u00f6senord \\u00f6verensst\\u00e4mmer.","Please make sure your postal code is %1 characters long.":"Se till att du har angett ett %1-symbols l\\u00e5ngt postnummer.","Please make sure your social security number is correct":"Kontrollera att ni har skrivit in ett korrekt personnummer","Please make sure your social security number is correct.":"Kontrollera att ni har skrivit in ett korrekt personnummer.","Please make sure your social security number is {0} or {1} numbers long.":"Se till att du har angett ett {0}-nummer eller {1}-nummer l\\u00e5ngt personnummer.","Please make sure your social security number is {0} symbols long.":"V\\u00e4nligen ange ditt personnummer med {0} siffror","Please rate the delivery.":"V\\u00e4nligen betygs\\u00e4tt leveransen.","Please select State\\/Province.":"V\\u00e4nligen v\\u00e4lj region.","Please select an option.":"V\\u00e4nligen v\\u00e4lj ett alternativ.","Please select one of the options.":"V\\u00e4nligen v\\u00e4lj ett av alternativen.","Please select store":"V\\u00e4nligen ange varuhus","Please specify a valid mobile number":"V\\u00e4nligen ange ett giltigt mobilnummer","Please specify a valid phone number":"V\\u00e4nligen ange ett giltigt telefonnummer","Please specify the quantity of product(s).":"V\\u00e4nligen ange antal av produkt(er).","Please try again with another form of payment.":"V\\u00e4nligen f\\u00f6rs\\u00f6k igen med ett annat betals\\u00e4tt.","Please upload {0} or less files.":"Sn\\u00e4lla ladda upp {0} eller mindre filer","Please use letters only (a-z or A-Z) in this field.":"V\\u00e4nligen anv\\u00e4nd endast bokst\\u00e4ver (a-\\u00f6 eller A-\\u00d6) i detta f\\u00e4lt.","Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.":"V\\u00e4nligen anv\\u00e4nd endast bokst\\u00e4ver (a-z eller A-Z) eller siffror (0-9) i detta f\\u00e4lt. Inga mellanslag eller andra tecken \\u00e4r till\\u00e5tna.","Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.":"V\\u00e4nligen anv\\u00e4nd endast bokst\\u00e4ver (a-z eller A-Z), siffror (0-9) eller mellanslag i detta f\\u00e4lt.","Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.":"V\\u00e4nligen anv\\u00e4nd endast bokst\\u00e4ver (a-z eller A-Z), siffror (0-9) eller understreck (_) i detta f\\u00e4lt, f\\u00f6rsta tecknet ska vara en bokstav.","Please use only letters (a-z or A-Z), numbers (0-9), spaces and \\"#\\" in this field.":"V\\u00e4nligen anv\\u00e4nd endast bokst\\u00e4ver (a-z eller A-Z), siffror (0-9), mellanslag och \\"#\\" i detta f\\u00e4lt.","Please use tag SCRIPT with SRC attribute or with proper content to include JavaScript to the document.":"V\\u00e4nligen anv\\u00e4nd taggen SCRIPT med SRC-attribut eller med korrekt inneh\\u00e5ll f\\u00f6r att inkludera JavaScript i dokumentet.","Please use this date format: dd\\/mm\\/yyyy. For example 17\\/03\\/2006 for the 17th of March, 2006.":"V\\u00e4nligen ange datum enligt formatet: dd\\/mm\\/\\u00e5\\u00e5\\u00e5\\u00e5. Exempelvis 17\\/03\\/2006 f\\u00f6r den 17:e mars, 2006.","Please wait...":"V\\u00e4nligen v\\u00e4nta...","Please, fill both fields with first and last name of the person who will pick up your order":"V\\u00e4nligen fyll i f\\u00f6r och efternamn p\\u00e5 den person som ska h\\u00e4mta best\\u00e4llningen","Please, fill both name and surname of the person":"V\\u00e4nligen skriv b\\u00e5de f\\u00f6rnamn och efternamn","Point camera at the QR Code below":"Rikta kameran mot QR-koden","Popular search terms":"Popul\\u00e4ra s\\u00f6kord","Postcode":"Postnummer","Previous":"F\\u00f6reg\\u00e5ende","Previous price:":"Tidigare pris:","Price":"Pris","Proceed":"Forts\\u00e4tt","Proceed to Checkout":"Till kassan","Product sheet":"Produktinformationsblad","Products":"Produkter","Provided Zip\\/Postal Code seems to be invalid.":"Angiven postkod verkar vara ogiltig.","Purchase Order Number":"Best\\u00e4llningsnummer","Qty":"Antal","Receive a copy by e-mail":"Skicka en e-postkopia till dig sj\\u00e4lv","Recent items":"Senaste artiklarna","Recently added item(s)":"Nyligen tillagda produkter","Related Products":"Andra k\\u00f6pte ocks\\u00e5","Reload captcha":"Ladda om captcha","Remember Me":"Kom ih\\u00e5g mig","Remove":"Ta bort","Remove all":"T\\u00f6m varukorg","Remove image":"Ta bort bild","Remove item":"Ta bort artikel","Replace":"Ers\\u00e4tt","Request for Bauhaus installation service":"BAUHAUS Montageservice Intresseanm\\u00e4lan","Reset":"Nollst\\u00e4ll","Review & Payments":"Granska & Betala","Saturday":"L\\u00f6rdag","Save":"Spara","Save Address":"Spara adress","Save for later use.":"Spara f\\u00f6r senare anv\\u00e4ndning.","Save in address book":"Spara i adressboken","Save new project":"Skapa nytt projekt","Search":"S\\u00f6k","Search by keyword":"S\\u00f6k p\\u00e5 nyckelord","Search for other ...":"S\\u00f6k","See Details":"Se detaljer","See all results":"S\\u00f6kresultat","See price":"Nytt l\\u00e4gre pris","See price before order confirmation.":"Se pris innan orderbekr\\u00e4ftelse.","Select":"V\\u00e4lj","Select All":"V\\u00e4lj alla","Select All on This Page":"V\\u00e4lj alla p\\u00e5 denna sida","Select Date":"V\\u00e4lj Datum","Select Method":"V\\u00e4lj s\\u00e4tt","Select Store":"V\\u00e4lj butik","Select all":"V\\u00e4lj alla","Select one project":"V\\u00e4lj ett projekt","Select the desired credit":"V\\u00e4lj \\u00f6nskad kredit","Select the discounted product and add it to the shopping cart":"V\\u00e4lj den rabatterade produkten och l\\u00e4gg till i kundvagnen","Select the product":"V\\u00e4lj produkten","Select time":"Tid","Select...":"V\\u00e4lj...","Selected":"Vald","Selected service point is not available. Please, choose another one from the list.":"Postombud \\u00e4r inte tillg\\u00e4ngligt. V\\u00e4nligen v\\u00e4lj ett annat fr\\u00e5n listan.","Send":"Skicka","Send Check to:":"Skicka check till:","Service points selection is not available for your postcode.":"Postombud \\u00e4r inte tillg\\u00e4ngligt f\\u00f6r ditt postnummer.","Ship Here":"Leverera Hit","Ship To:":"Leverera till:","Shipping":"Frakt","Shipping Address":"Leveransadress","Shipping Method":"Leveranss\\u00e4tt","Shipping Method:":"Leveranss\\u00e4tt:","Shipping Methods":"Frakts\\u00e4tt","Show":"Visa","Show all":"Visa allt","Show all...":"Visa alla...","Show less":"Visa mindre","Show more":"Visa fler","Show more locations":"Visa fler postombud","Show more products":"Visa fler produkter","Show previous results":"Visa fler produkter","Showing %1 results for ":"%1 s\\u00f6kresultat f\\u00f6r ","Sign In":"Logga in","Social Security Number":"Personnummer","Some problems occurred during this request. Please, try again later.":"Ett problem uppstod vid f\\u00f6rfr\\u00e5gan. V\\u00e4nligen f\\u00f6rs\\u00f6k igen senare.","Something went wrong":"N\\u00e5got gick fel","Something went wrong.":"N\\u00e5got gick fel.","Sorry, but something went wrong.":"Tyv\\u00e4rr, n\\u00e5got gick fel.","Sorry, something went wrong. Please try again later.":"F\\u00f6rl\\u00e5t, n\\u00e5got gick fel. V\\u00e4nligen f\\u00f6rs\\u00f6k igen senare.","Sorry, the information is temporarily unavailable. Please, try later.":"Beklagar, informationen \\u00e4r inte tillg\\u00e4nglig f\\u00f6r tillf\\u00e4llet. V\\u00e4nligen f\\u00f6rs\\u00f6k senare.","Sort by:":"Sorterar p\\u00e5:","Store":"Butik","Store Credit":"Butikskredit","Strong":"Starkt","Submit":"Skicka","Subtotal":"Orderv\\u00e4rde","Summary":"Sammanfattning","Sunday":"S\\u00f6ndag","Tap Scan QR Code button":"Tryck p\\u00e5 Skanna QR-kod","Tax":"Moms","Technical problem occurred. Please try again later.":"Ett tekniskt problem intr\\u00e4ffade. Var god f\\u00f6rs\\u00f6k igen senare.","Telephone":"Telefonnummer","Terms and Conditions BAUHAUS My account":"Allm\\u00e4nna villkor BAUHAUS Mitt konto","Thank you, %1!":"Tack, %1!","The amount is not within the specified range.":"Beloppet \\u00e4r inte inom angivet intervall","The date is not within the specified range.":"Datumet \\u00e4r inte inom det specificerade intervallet.","The document will open in the new browser window, do you want to continue?":"Dokumentet kommer att \\u00f6ppnas i en ny flik, vill du forts\\u00e4tta?","The fewest you may purchase is %1.":"L\\u00e4gst antal f\\u00f6r k\\u00f6p \\u00e4r %1.","The field isn\'t complete.":"F\\u00e4ltet \\u00e4r inte komplett.","The value is not within the specified range.":"V\\u00e4rdet \\u00e4r inte inom angivet intervall.","This is a required field.":"Detta \\u00e4r ett obligatoriskt f\\u00e4lt.","This product is out of stock.":"Denna produkt finns inte i lager.","This tab contains invalid data. Please resolve this before saving.":"Den h\\u00e4r fliken inneh\\u00e5ller ogiltiga data. L\\u00f6s detta innan du sparar.","Thursday":"Torsdag","To check out, please sign in with your email address.":"F\\u00f6r att avsluta k\\u00f6pet, logga in med din e-postadress.","To:":"Till:","Total":"Totalsumma","Total incl. tax":"Totalsumma inkl. moms","Translate":"\\u00d6vers\\u00e4tt","Try again":"Prova igen","Tuesday":"Tisdag","Unable to clean the manual order session":"Det gick inte att reng\\u00f6ra den manuella ordersessionen","Update":"Uppdatera","Upload":"Ladda upp","Upload more":"Ladda upp fler","Use Default Value":"Anv\\u00e4nd f\\u00f6rvalt v\\u00e4rde","Use Mobile BankID on another device":"Anv\\u00e4nd BankID p\\u00e5 en annan enhet","Use tax deduction":"Anv\\u00e4nd Rot-Avdrag\\/Gr\\u00f6n Teknik","Username":"Anv\\u00e4ndarnamn","Verify with BankID to continue":"Verifiera med BankID f\\u00f6r att forts\\u00e4tta.","Very Strong":"Mycket Starkt","View Details":"Se detaljer","View and Edit Cart":"Visa och redigera varukorg","View {{nbHits}} products":"Visar {{nbHits}} produkter","Vul hier een geldige datum in.":"V\\u00e4nligen ange ett giltigt datum h\\u00e4r.","Warning":"Varning","We also sent you a copy of the request to your email.":"Vi har \\u00e4ven skickat en kopia av beg\\u00e4ran till din e-post.","We can\'t create the Wish List right now.":"Vi kan inte skapa \\u00f6nskelistan just nu.","We couldn\'t find any records.":"Vi kunde inte hitta n\\u00e5gra uppgifter.","We don\'t recognize or support this file extension type.":"Vi k\\u00e4nner inte igen eller st\\u00f6djer denna fil\\u00e4ndelsetypen.","We have registered your request.":"Vi har registrerat din beg\\u00e4ran.","We will contact you via the contact details you have provided in your application.":"Vi kommer att kontakta dig via de kontaktuppgifter du angivit i din ans\\u00f6kan.","Weak":"Svagt","Wednesday":"Onsdag","Week":"Vecka","Welcome, %1!":"V\\u00e4lkommen, %1!","What can we help you with?":"Vad kan vi hj\\u00e4lpa dig med?","What can we help you with? Describe your opinion":"Vad kan vi hj\\u00e4lpa dig med?","What do you need help with?":"Vad beh\\u00f6ver du hj\\u00e4lp med? ","What is PayPal?":"Vad \\u00e4r PayPal?","What is this?":"Vad \\u00e4r detta?","What\'s this?":"Vad \\u00e4r detta?","Year":"\\u00c5r","Yes":"Ja","You already have an account with us. Sign in or continue as guest.":"Du har redan ett konto hos oss. Logga in eller forts\\u00e4tt som g\\u00e4st.","You are in {1}. You are logged in as {2}.":"Ditt varuhus \\u00e4r {1}. Du \\u00e4r inloggad som {2}","You can buy this product only in quantities of %1 at a time.":"Online s\\u00e4ljs denna vara i antal av %1 st \\u00e5t g\\u00e5ngen","You can create an account after checkout.":"Du kan skapa ett konto efter att k\\u00f6pet \\u00e4r klart.","You can track your order status by creating an account.":"Du kan sp\\u00e5ra din orderstatus genom att skapa ett nytt konto.","You cart will be converted to selected delivery type after adding product to the cart":"Din varukorg kommer att \\u00e4ndras till valt leveranss\\u00e4tt efter att produkten lagts i din varukorg","You do not have enough bonus points":"Du har inte tillr\\u00e4ckligt med bonuspo\\u00e4ng","You have chosen home delivery.":"Du har valt hemleverans.","You have chosen pick-up.":"Du har valt upph\\u00e4mtning.","You have no items in your shopping cart.":"Du har inga produkter i din varukorg.","You have successfully saved your edits.":"Du har sparat dina \\u00e4ndringar.","You haven\'t selected any items!":"Du har inte valt n\\u00e5gra artiklar!","You may also be interested in these products":"Du kan ocks\\u00e5 vara intresserad av dessa produkter","You save:":"Du sparar:","You will be redirected to the PayPal website when you place an order.":"N\\u00e4r du \\u00e4r klar med best\\u00e4llningen f\\u00f6rflyttas du till PayPal.","You\'re currently reading page":"Du l\\u00e4ser just nu sida","Your %s %s was added.":"Ditt %s %s \\u00e4r tillagt!","Your Full Name":"Fullst\\u00e4ndigt namn","Your Store":"Ditt varuhus","Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx":"Ditt postnummer m\\u00e5ste vara inom 902xx-xxxx till 905-xx-xxxx","Your cart":"Din varukorg","Your coupon was successfully applied.":"Din kupong lades till.","Your coupon was successfully removed.":"Din kupong togs bort.","edit":"\\u00e4ndra","ending":"avslutar","expires":"utg\\u00e5r","from":"Fr\\u00e5n","more":"mer","of":"av","options":"alternativ","or":"eller","per page":"per sida","records found":"artiklar hittades","select all":"v\\u00e4lj alla","selected":"vald","to":"till","unselect all":"Avmarkera alla"}';});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Translation/js/mage-translation-dictionary',[
    'text!js-translation.json'
], function (dict) {
    'use strict';

    return JSON.parse(dict);
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/translate',[
    'jquery',
    'mage/mage',
    'mageTranslationDictionary',
    'underscore'
], function ($, mage, dictionary, _) {
    'use strict';

    $.extend(true, $, {
        mage: {
            translate: (function () {
                /**
                 * Key-value translations storage
                 * @type {Object}
                 * @private
                 */
                var _data = dictionary;

                return {
                    /**
                     * Add new translation (two string parameters) or several translations (object)
                     */
                    add: function () {
                        if (arguments.length > 1) {
                            _data[arguments[0]] = arguments[1];
                        } else if (typeof arguments[0] === 'object') {
                            $.extend(_data, arguments[0]);
                        }
                    },

                    /**
                     * Make a translation with parsing (to handle case when _data represents tuple)
                     * @param {String} text
                     * @return {String}
                     */
                    translate: function (text) {
                        return typeof _data[text] !== 'undefined' ? _data[text] : text;
                    }
                };
            }())
        }
    });
    $.mage.__ = $.proxy($.mage.translate.translate, $.mage.translate);

    // Provide i18n wrapper to be used in underscore templates for translation
    _.extend(_, {
        /**
         * Make a translation using $.mage.__
         *
         * @param {String} text
         * @return {String}
         */
        i18n: function (text) {
            return $.mage.__(text);
        }
    });

    return $.mage.__;
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausSe/js/mixins/validation', ['jquery', 'mage/translate'], ($, $t) => {'use strict';
    return function (target) {
        $.validator.addMethod(
            'validate-email',
            //In the domain part we explicitely restrict  'xn--' combination, because browser transforms any non-ASCII characters to ASCII-like that starts with 'xn--'
            function (value) {
                return !value || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-])+)*@((?!.*xn--)[a-z0-9-]+(\.(?!xn--)[a-z0-9-]+)*\.[a-z]{2,})$/i.test(value);
            },
            $t('Please enter a valid email address (Ex: johndoe@domain.com).')
        );
        return target;
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/collapsible',[
    'jquery',
    'jquery-ui-modules/widget',
    'jquery-ui-modules/core',
    'jquery/jquery-storageapi',
    'mage/mage'
], function ($) {
    'use strict';

    var hideProps = {},
        showProps = {};

    hideProps.height = 'hide';
    showProps.height = 'show';

    $.widget('mage.collapsible', {
        options: {
            active: false,
            disabled: false,
            collapsible: true,
            header: '[data-role=title]',
            content: '[data-role=content]',
            trigger: '[data-role=trigger]',
            closedState: null,
            openedState: null,
            disabledState: null,
            ajaxUrlElement: '[data-ajax=true]',
            ajaxContent: false,
            loadingClass: null,
            saveState: false,
            animate: false,
            icons: {
                activeHeader: null,
                header: null
            },
            collateral: {
                element: null,
                openedState: null
            }
        },

        /**
         * @private
         */
        _create: function () {
            this.storage = $.localStorage;
            this.icons = false;

            if (typeof this.options.icons === 'string') {
                this.options.icons = JSON.parse(this.options.icons);
            }

            this._processPanels();
            this._processState();
            this._refresh();

            if (this.options.icons.header && this.options.icons.activeHeader) {
                this._createIcons();
                this.icons = true;
            }

            this.element.on('dimensionsChanged', function (e) {
                if (e.target && e.target.classList.contains('active')) {
                    this._scrollToTopIfNotVisible();
                }
            }.bind(this));

            this._bind('click');
            this._trigger('created');
        },

        /**
         * @private
         */
        _refresh: function () {
            this.trigger.attr('tabIndex', 0);

            if (this.options.active && !this.options.disabled) {
                if (this.options.openedState) {
                    this.element.addClass(this.options.openedState);
                }

                if (this.options.collateral.element && this.options.collateral.openedState) {
                    $(this.options.collateral.element).addClass(this.options.collateral.openedState);
                }

                if (this.options.ajaxContent) {
                    this._loadContent();
                }
                // ARIA (updates aria attributes)
                this.header.attr({
                    'aria-selected': false
                });
            } else if (this.options.disabled) {
                this.disable();
            } else {
                this.content.hide();

                if (this.options.closedState) {
                    this.element.addClass(this.options.closedState);
                }
            }
        },

        /**
         * Processing the state:
         *     If deep linking is used and the anchor is the id of the content or the content contains this id,
         *     and the collapsible element is a nested one having collapsible parents, in order to see the content,
         *     all the parents must be expanded.
         * @private
         */
        _processState: function () {
            var anchor = window.location.hash,
                isValid = $.mage.isValidSelector(anchor),
                urlPath = window.location.pathname.replace(/\./g, ''),
                state;

            this.stateKey = encodeURIComponent(urlPath + this.element.attr('id'));

            if (isValid &&
                ($(this.content.find(anchor)).length > 0 || this.content.attr('id') === anchor.replace('#', ''))
            ) {
                this.element.parents('[data-collapsible=true]').collapsible('forceActivate');

                if (!this.options.disabled) {
                    this.options.active = true;

                    if (this.options.saveState) { //eslint-disable-line max-depth
                        this.storage.set(this.stateKey, true);
                    }
                }
            } else if (this.options.saveState && !this.options.disabled) {
                state = this.storage.get(this.stateKey);

                if (typeof state === 'undefined' || state === null) {
                    this.storage.set(this.stateKey, this.options.active);
                } else if (state === true) {
                    this.options.active = true;
                } else if (state === false) {
                    this.options.active = false;
                }
            }
        },

        /**
         * @private
         */
        _createIcons: function () {
            var icons = this.options.icons;

            if (icons) {
                $('<span>')
                    .addClass(icons.header)
                    .attr('data-role', 'icons')
                    .prependTo(this.header);

                if (this.options.active && !this.options.disabled) {
                    this.header.children('[data-role=icons]')
                        .removeClass(icons.header)
                        .addClass(icons.activeHeader);
                }
            }
        },

        /**
         * @private
         */
        _destroyIcons: function () {
            this.header
                .children('[data-role=icons]')
                .remove();
        },

        /**
         * @private
         */
        _destroy: function () {
            var options = this.options;

            this.element.removeAttr('data-collapsible');

            this.trigger.removeAttr('tabIndex');

            if (options.openedState) {
                this.element.removeClass(options.openedState);
            }

            if (this.options.collateral.element && this.options.collateral.openedState) {
                $(this.options.collateral.element).removeClass(this.options.collateral.openedState);
            }

            if (options.closedState) {
                this.element.removeClass(options.closedState);
            }

            if (options.disabledState) {
                this.element.removeClass(options.disabledState);
            }

            if (this.icons) {
                this._destroyIcons();
            }
        },

        /**
         * @private
         */
        _processPanels: function () {
            var headers, triggers;

            this.element.attr('data-collapsible', 'true');

            if (typeof this.options.header === 'object') {
                this.header = this.options.header;
            } else {
                headers = this.element.find(this.options.header);

                if (headers.length > 0) {
                    this.header = headers.eq(0);
                } else {
                    this.header = this.element;
                }
            }

            if (typeof this.options.content === 'object') {
                this.content = this.options.content;
            } else {
                this.content = this.header.next(this.options.content).eq(0);
            }

            // ARIA (init aria attributes)
            if (this.header.attr('id')) {
                this.content.attr('aria-labelledby', this.header.attr('id'));
            }

            if (this.content.attr('id')) {
                this.header.attr('aria-controls', this.content.attr('id'));
            }

            this.header
                .attr({
                    'role': 'tab',
                    'aria-selected': this.options.active,
                    'aria-expanded': this.options.active
                });

            // For collapsible widget only (not tabs or accordion)
            if (this.header.parent().attr('role') !== 'presentation') {
                this.header
                    .parent()
                    .attr('role', 'tablist');
            }

            this.content.attr({
                'role': 'tabpanel',
                'aria-hidden': !this.options.active
            });

            if (typeof this.options.trigger === 'object') {
                this.trigger = this.options.trigger;
            } else {
                triggers = this.header.find(this.options.trigger);

                if (triggers.length > 0) {
                    this.trigger = triggers.eq(0);
                } else {
                    this.trigger = this.header;
                }
            }
        },

        /**
         * @param {jQuery.Event} event
         * @private
         */
        _keydown: function (event) {
            var keyCode;

            if (event.altKey || event.ctrlKey) {
                return;
            }

            keyCode = $.ui.keyCode;

            switch (event.keyCode) {
                case keyCode.SPACE:
                case keyCode.ENTER:
                    this._eventHandler(event);
                    break;
            }

        },

        /**
         * @param {jQuery.Event} event
         * @private
         */
        _bind: function (event) {
            var self = this;

            this.events = {
                keydown: '_keydown'
            };

            if (event) {
                $.each(event.split(' '), function (index, eventName) {
                    self.events[eventName] = '_eventHandler';
                });
            }
            this._off(this.trigger);

            if (!this.options.disabled) {
                this._on(this.trigger, this.events);
            }
        },

        /**
         * Disable.
         */
        disable: function () {
            this.options.disabled = true;
            this._off(this.trigger);
            this.forceDeactivate();

            if (this.options.disabledState) {
                this.element.addClass(this.options.disabledState);
            }
            this.trigger.attr('tabIndex', -1);
        },

        /**
         * Enable.
         */
        enable: function () {
            this.options.disabled = false;
            this._on(this.trigger, this.events);
            this.forceActivate();

            if (this.options.disabledState) {
                this.element.removeClass(this.options.disabledState);
            }
            this.trigger.attr('tabIndex', 0);
        },

        /**
         * @param {jQuery.Event} event
         * @private
         */
        _eventHandler: function (event) {

            if (this.options.active && this.options.collapsible) {
                this.deactivate();
            } else {
                this.activate();

            }
            event.preventDefault();

        },

        /**
         * @param {*} prop
         * @private
         */
        _animate: function (prop) {
            var duration,
                easing,
                animate = this.options.animate;

            if (typeof animate === 'number') {
                duration = animate;
            }

            if (typeof animate === 'string') {
                animate = JSON.parse(animate);
            }
            duration = duration || animate.duration;
            easing = animate.easing;
            this.content.animate(prop, duration, easing);
        },

        /**
         * Deactivate.
         */
        deactivate: function () {
            if (this.options.animate) {
                this._animate(hideProps);
            } else {
                this.content.hide();
            }
            this._close();
        },

        /**
         * Force deactivate.
         */
        forceDeactivate: function () {
            this.content.hide();
            this._close();

        },

        /**
         * @private
         */
        _close: function () {
            this.options.active = false;

            if (this.options.saveState) {
                this.storage.set(this.stateKey, false);
            }

            if (this.options.openedState) {
                this.element.removeClass(this.options.openedState);
            }

            if (this.options.collateral.element && this.options.collateral.openedState) {
                $(this.options.collateral.element).removeClass(this.options.collateral.openedState);
            }

            if (this.options.closedState) {
                this.element.addClass(this.options.closedState);
            }

            if (this.icons) {
                this.header.children('[data-role=icons]')
                    .removeClass(this.options.icons.activeHeader)
                    .addClass(this.options.icons.header);
            }

            // ARIA (updates aria attributes)
            this.header.attr({
                'aria-selected': 'false',
                'aria-expanded': 'false'
            });
            this.content.attr({
                'aria-hidden': 'true'
            });

            this.element.trigger('dimensionsChanged', {
                opened: false
            });
        },

        /**
         * Activate.
         *
         * @return void;
         */
        activate: function () {
            if (this.options.disabled) {
                return;
            }

            if (this.options.animate) {
                this._animate(showProps);
            } else {
                this.content.show();
            }
            this._open();
        },

        /**
         * Force activate.
         */
        forceActivate: function () {
            if (!this.options.disabled) {
                this.content.show();
                this._open();
            }
        },

        /**
         * @private
         */
        _open: function () {
            this.element.trigger('beforeOpen');
            this.options.active = true;

            if (this.options.ajaxContent) {
                this._loadContent();
            }

            if (this.options.saveState) {
                this.storage.set(this.stateKey, true);
            }

            if (this.options.openedState) {
                this.element.addClass(this.options.openedState);
            }

            if (this.options.collateral.element && this.options.collateral.openedState) {
                $(this.options.collateral.element).addClass(this.options.collateral.openedState);
            }

            if (this.options.closedState) {
                this.element.removeClass(this.options.closedState);
            }

            if (this.icons) {
                this.header.children('[data-role=icons]')
                    .removeClass(this.options.icons.header)
                    .addClass(this.options.icons.activeHeader);
            }

            // ARIA (updates aria attributes)
            this.header.attr({
                'aria-selected': 'true',
                'aria-expanded': 'true'
            });
            this.content.attr({
                'aria-hidden': 'false'
            });

            this.element.trigger('dimensionsChanged', {
                opened: true
            });
        },

        /**
         * @private
         */
        _loadContent: function () {
            var url = this.element.find(this.options.ajaxUrlElement).attr('href'),
                that = this;

            if (url) {
                that.xhr = $.get({
                    url: url,
                    dataType: 'html'
                }, function () {
                });
            }

            if (that.xhr && that.xhr.statusText !== 'canceled') {
                if (that.options.loadingClass) {
                    that.element.addClass(that.options.loadingClass);
                }
                that.content.attr('aria-busy', 'true');
                that.xhr.done(function (response) {
                    setTimeout(function () {
                        that.content.html(response);
                    }, 1);
                });
                that.xhr.always(function (jqXHR, status) {
                    setTimeout(function () {
                        if (status === 'abort') {
                            that.content.stop(false, true);
                        }

                        if (that.options.loadingClass) {
                            that.element.removeClass(that.options.loadingClass);
                        }
                        that.content.removeAttr('aria-busy');

                        if (jqXHR === that.xhr) {
                            delete that.xhr;
                        }
                    }, 1);
                });
            }
        },

        /**
         * @private
         */
        _scrollToTopIfNotVisible: function () {
            if (this._isElementOutOfViewport()) {
                this.header[0].scrollIntoView();
            }
        },

        /**
         * @private
         * @return {Boolean}
         */
        _isElementOutOfViewport: function () {
            var headerRect = this.header[0].getBoundingClientRect(),
                contentRect = this.content.get().length ? this.content[0].getBoundingClientRect() : false,
                headerOut,
                contentOut;

            headerOut = headerRect.bottom - headerRect.height < 0 ||
                headerRect.right - headerRect.width < 0 ||
                headerRect.left + headerRect.width > window.innerWidth ||
                headerRect.top + headerRect.height > window.innerHeight;

            contentOut = contentRect ? contentRect.bottom - contentRect.height < 0 ||
                contentRect.right - contentRect.width < 0 ||
                contentRect.left + contentRect.width > window.innerWidth ||
                contentRect.top + contentRect.height > window.innerHeight : false;

            return headerOut ? headerOut : contentOut;
        }
    });

    return $.mage.collapsible;
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('Vaimo_BauhausSe/js/mixins/tabs', [
    'jquery',
    'mage/collapsible',
    'jquery/ui'
], function ($, collapsible) {
    'use strict';

    return function (originalWidget) {
        $.widget(originalWidget.prototype.namespace + '.' + originalWidget.prototype.widgetName,
            $[originalWidget.prototype.namespace][originalWidget.prototype.widgetName],
            {
                options: {
                    filterIdAttr: ''
                },

                _destroy: function () {
                    this._super();

                    $.each(this.collapsibles, function () {
                        $(this).off('beforeOpen');
                    });
                },

                openByElement: function ($el) {
                    var $collapsible = this.collapsibles.filter($el);
                    if ($collapsible.length) {
                        $collapsible.collapsible('activate');
                    }
                },

                closeByElement: function ($el) {
                    var $collapsible = this.collapsibles.filter($el);
                    if ($collapsible.length) {
                        $collapsible.collapsible('deactivate');
                    }
                },

                setActiveCollapsibleByCollapsibleId: function (id) {
                    var collapsibleWidget = this.getCollapsibleById(id);
                    if (!collapsibleWidget) {return}
                    collapsibleWidget.activate();
                },

                deactivateCollapsibleByCollapsibleId: function (id) {
                    var collapsibleWidget = this.getCollapsibleById(id);
                    if (!collapsibleWidget) {return}
                    collapsibleWidget.deactivate();
                },

                getCollapsibleById: function(id) {
                    if (!id) {return}

                    for (var i = 0; i < this.collapsibleWidgets.length; i++) {
                        if (this.collapsibleWidgets[i].element.attr(this.options.filterIdAttr) === id) {
                            return this.collapsibleWidgets[i];
                        }
                    }
                },

                getActiveCollapsibleId: function () {
                    for (var i = 0; i < this.collapsibleWidgets.length; i++) {
                        if (this.collapsibleWidgets[i].options.active) {
                            return this.collapsibleWidgets[i].element.attr(this.options.filterIdAttr);
                        }
                    }

                    return '';
                },

                _instantiateCollapsible: function (element, index, active, disabled) {
                    if (!this.collapsibleWidgets) {
                        this.collapsibleWidgets = [];
                    }

                    this.collapsibleWidgets.push(collapsible($.extend({}, this.options, {
                            active: active,
                            disabled: disabled,
                            header: this.headers.eq(index),
                            content: this.contents.eq(index),
                            trigger: this.triggers.eq(index)
                        }),
                        element
                    ));
                },
            });

        return $[originalWidget.prototype.namespace][originalWidget.prototype.widgetName];
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('Vaimo_BauhausSe/js/mixins/collapsible', [
    'jquery',
    'cssTimeout',
    'jquery/ui'
], function ($, cssTimeout) {
    'use strict';

    return function (originalWidget) {
        var prototype = originalWidget.prototype;

        $.widget(prototype.namespace + '.' + prototype.widgetName,
            $[prototype.namespace][prototype.widgetName],
            {
                options: {
                    focusBottom: false,
                    $scrollToBottomElement: null,
                    focusTop: false,
                },

                /**
                 * Animate callback
                 */
                animateCallback: function(prop) {
                    this.element.trigger('dimensionsChangedDone');

                    if (this.options.focusBottom && this.options.$scrollToBottomElement && prop.height === 'show') {
                        $('html, body').animate({
                            scrollTop: this.options.$scrollToBottomElement.offset().top + this.options.$scrollToBottomElement.height() + 'px'
                        }, cssTimeout);
                    }

                    if (this.options.focusTop && prop.height === 'show') {
                        setTimeout(function(){
                            this.element[0].scrollIntoView();
                        }.bind(this), cssTimeout);
                    }
                },

                /**
                 * Add animate
                 */
                _animate: function (prop) {
                    var duration,
                        easing,
                        animate = this.options.animate;

                    if (typeof animate === 'number') {
                        duration = animate;
                    }

                    if (typeof animate === 'string') {
                        animate = JSON.parse(animate);
                    }
                    duration = duration || animate.duration;
                    easing = animate.easing;

                    this.content.animate(prop, duration, easing, this.animateCallback.bind(this, prop));
                },

                /**
                 * The difference from parent method is removing of ! inside condition that was added in 2.3.3 and looks like a bug,
                 * cause in that case the method name and what is does is not valid. The item is scrolled when it is NOT out of viewport actually
                 * @param {HTMLElement} elem
                 * @private
                 */
                _scrollToTopIfVisible: function (elem) {
                    if (this._isElementOutOfViewport(elem)) {
                        elem.scrollIntoView();
                    }
                },
            });

        return $[prototype.namespace][prototype.widgetName];
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausSe/js/mixins/library/core/collection-mixin', [], () => {'use strict';
return (uiComponent) => uiComponent.extend({
    getChildElemByIndex: function (index) {
        return this.elems().filter(el => el.index === index);
    },

    getChildElemByGroup: function (groupName) {
        return this.elems().filter(el => el.groupName === groupName);
    }
})});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausSe/js/mixins/lib/knockout/template/renderer', [], () => {
'use strict';

return (target) => {
    const selfClosingTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
    const tagNameRegexp = /\w+/;
    const tagRegexp = /(<[^>]+\/>)/ig;
    const tagEndSlashRegexp = /\/>$/;
    const orig = target.parseTemplate;
    target.parseTemplate = function(html) {
        return orig.apply(target, [target.fixCustomSelfClosingNodes(html)].concat(Array.prototype.splice.call(arguments, 1, arguments.length)));
    };

    target.fixCustomSelfClosingNodes = function(htmlString) {
        try {
            return htmlString.replace(tagRegexp, tag => {
                const tagName = tag.match(tagNameRegexp);

                if (!(tagName && tagName[0])) {
                    console.error('tagName match failed on ' + tag);
                    return tag;
                }
                if (selfClosingTags.indexOf(tagName[0]) !== -1) return tag;

                return tag.replace(tagEndSlashRegexp, '>') + '</' + tagName[0] + '>';
            });
        }catch(e) {
            try {

            }catch (e) {
                console.error(e);
                return htmlString;
            }
        }
    }

    return target;
}
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Vaimo_BauhausSe/js/mixins/view/theme-messages-mixin', ['ko'], ko => {'use strict';
return target => target.extend({
initialize: function () {
    this._super();

    this.observe({commonOutputMessages: this._getCommonMessages(), messagesIds: []});
    this.aggregatedMessages = ko.observableArray([
        () => this.cookieMessages ? this.cookieMessages : [],
        this.commonOutputMessages
    ]);
    this.messages.subscribe(() => {
        this.messagesIds([]);
        this.commonOutputMessages(this._getCommonMessages());
    });
},

addMessage: function(msg) {
    if (!this.additionalMessages) {
        this.additionalMessages = ko.observableArray([]);
        this.aggregatedMessages.push(this.additionalMessages);
    }

    this.additionalMessages.push(msg);
    window.scrollTo(0, 0);
},

_getCommonMessages: function() {
    return this.messages().messages || [];
},

setMessageId: function (idx) {
    const msgIds = this.messagesIds();
    msgIds.push(idx);
    this.messagesIds(msgIds);
}
})});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/utils/strings',[
    'underscore'
], function (_) {
    'use strict';

    var jsonRe = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;

    return {

        /**
         * Attempts to convert string to one of the primitive values,
         * or to parse it as a valid json object.
         *
         * @param {String} str - String to be processed.
         * @returns {*}
         */
        castString: function (str) {
            try {
                str = str === 'true' ? true :
                    str === 'false' ? false :
                        str === 'null' ? null :
                            +str + '' === str ? +str :
                                jsonRe.test(str) ? JSON.parse(str) :
                                    str;
            } catch (e) {
            }

            return str;
        },

        /**
         * Splits string by separator if it's possible,
         * otherwise returns the incoming value.
         *
         * @param {(String|Array|*)} str - String to split.
         * @param {String} [separator=' '] - Seperator based on which to split the string.
         * @returns {Array|*} Splitted string or the incoming value.
         */
        stringToArray: function (str, separator) {
            separator = separator || ' ';

            return typeof str === 'string' ?
                str.split(separator) :
                str;
        },

        /**
         * Converts the incoming string which consists
         * of a specified delimiters into a format commonly used in form elements.
         *
         * @param {String} name - The incoming string.
         * @param {String} [separator='.']
         * @returns {String} Serialized string.
         *
         * @example
         *      utils.serializeName('one.two.three');
         *      => 'one[two][three]';
         */
        serializeName: function (name, separator) {
            var result;

            separator = separator || '.';
            name = name.split(separator);

            result = name.shift();

            name.forEach(function (part) {
                result += '[' + part + ']';
            });

            return result;
        },

        /**
         * Checks wether the incoming value is not empty,
         * e.g. not 'null' or 'undefined'
         *
         * @param {*} value - Value to check.
         * @returns {Boolean}
         */
        isEmpty: function (value) {
            return value === '' || _.isUndefined(value) || _.isNull(value);
        },

        /**
         * Adds 'prefix' to the 'part' value if it was provided.
         *
         * @param {String} prefix
         * @param {String} part
         * @returns {String}
         */
        fullPath: function (prefix, part) {
            return prefix ? prefix + '.' + part : part;
        },

        /**
         * Splits incoming string and returns its' part specified by offset.
         *
         * @param {String} parts
         * @param {Number} [offset]
         * @param {String} [delimiter=.]
         * @returns {String}
         */
        getPart: function (parts, offset, delimiter) {
            delimiter = delimiter || '.';
            parts = parts.split(delimiter);
            offset = this.formatOffset(parts, offset);

            parts.splice(offset, 1);

            return parts.join(delimiter) || '';
        },

        /**
         * Converts nameThroughCamelCase to name-through-minus
         *
         * @param {String} string
         * @returns {String}
         */
        camelCaseToMinus: function camelCaseToMinus(string) {
            return ('' + string)
                .split('')
                .map(function (symbol, index) {
                    return index ?
                        symbol.toUpperCase() === symbol ?
                        '-' + symbol.toLowerCase() :
                            symbol :
                        symbol.toLowerCase();
                })
                .join('');
        },

        /**
         * Converts name-through-minus to nameThroughCamelCase
         *
         * @param {String} string
         * @returns {String}
         */
        minusToCamelCase: function minusToCamelCase(string) {
            return ('' + string)
                .split('-')
                .map(function (part, index) {
                    return index ? part.charAt(0).toUpperCase() + part.slice(1) : part;
                })
                .join('');
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/utils/arrays',[
    'underscore',
    './strings'
], function (_, utils) {
    'use strict';

    /**
     * Defines index of an item in a specified container.
     *
     * @param {*} item - Item whose index should be defined.
     * @param {Array} container - Container upon which to perform search.
     * @returns {Number}
     */
    function getIndex(item, container) {
        var index = container.indexOf(item);

        if (~index) {
            return index;
        }

        return _.findIndex(container, function (value) {
            return value && value.name === item;
        });
    }

    return {
        /**
         * Facade method to remove/add value from/to array
         * without creating a new instance.
         *
         * @param {Array} arr - Array to be modified.
         * @param {*} value - Value to add/remove.
         * @param {Boolean} add - Flag that specfies operation.
         * @returns {Utils} Chainable.
         */
        toggle: function (arr, value, add) {
            return add ?
                this.add(arr, value) :
                this.remove(arr, value);
        },

        /**
         * Removes the incoming value from array in case
         * without creating a new instance of it.
         *
         * @param {Array} arr - Array to be modified.
         * @param {*} value - Value to be removed.
         * @returns {Utils} Chainable.
         */
        remove: function (arr, value) {
            var index = arr.indexOf(value);

            if (~index) {
                arr.splice(index, 1);
            }

            return this;
        },

        /**
         * Adds the incoming value to array if
         * it's not alredy present in there.
         *
         * @param {Array} arr - Array to be modifed.
         * @param {...*} arguments - Values to be added.
         * @returns {Utils} Chainable.
         */
        add: function (arr) {
            var values = _.toArray(arguments).slice(1);

            values.forEach(function (value) {
                if (!~arr.indexOf(value)) {
                    arr.push(value);
                }
            });

            return this;
        },

        /**
         * Inserts specified item into container at a specified position.
         *
         * @param {*} item - Item to be inserted into container.
         * @param {Array} container - Container of items.
         * @param {*} [position=-1] - Position at which item should be inserted.
         *      Position can represent:
         *          - specific index in container
         *          - item which might already be present in container
         *          - structure with one of these properties: after, before
         * @returns {Boolean|*}
         *      - true if element has changed its' position
         *      - false if nothing has changed
         *      - inserted value if it wasn't present in container
         */
        insert: function (item, container, position) {
            var currentIndex = getIndex(item, container),
                newIndex,
                target;

            if (typeof position === 'undefined') {
                position = -1;
            } else if (typeof position === 'string') {
                position = isNaN(+position) ? position : +position;
            }

            newIndex = position;

            if (~currentIndex) {
                target = container.splice(currentIndex, 1)[0];

                if (typeof item === 'string') {
                    item = target;
                }
            }

            if (typeof position !== 'number') {
                target = position.after || position.before || position;

                newIndex = getIndex(target, container);

                if (~newIndex && (position.after || newIndex >= currentIndex)) {
                    newIndex++;
                }
            }

            if (newIndex < 0) {
                newIndex += container.length + 1;
            }

            container[newIndex] ?
                container.splice(newIndex, 0, item) :
                container[newIndex] = item;

            return !~currentIndex ? item : currentIndex !== newIndex;
        },

        /**
         * @param {Array} elems
         * @param {Number} offset
         * @return {Number|*}
         */
        formatOffset: function (elems, offset) {
            if (utils.isEmpty(offset)) {
                offset = -1;
            }

            offset = +offset;

            if (offset < 0) {
                offset += elems.length + 1;
            }

            return offset;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/utils/objects',[
    'ko',
    'jquery',
    'underscore',
    'mage/utils/strings'
], function (ko, $, _, stringUtils) {
    'use strict';

    var primitives = [
        'undefined',
        'boolean',
        'number',
        'string'
    ];

    /**
     * Sets nested property of a specified object.
     * @private
     *
     * @param {Object} parent - Object to look inside for the properties.
     * @param {Array} path - Splitted path the property.
     * @param {*} value - Value of the last property in 'path' array.
     * returns {*} New value for the property.
     */
    function setNested(parent, path, value) {
        var last = path.pop(),
            len = path.length,
            pi = 0,
            part = path[pi];

        for (; pi < len; part = path[++pi]) {
            if (!_.isObject(parent[part])) {
                parent[part] = {};
            }

            parent = parent[part];
        }

        if (typeof parent[last] === 'function') {
            parent[last](value);
        } else {
            parent[last] = value;
        }

        return value;
    }

    /**
     * Retrieves value of a nested property.
     * @private
     *
     * @param {Object} parent - Object to look inside for the properties.
     * @param {Array} path - Splitted path the property.
     * @returns {*} Value of the property.
     */
    function getNested(parent, path) {
        var exists = true,
            len = path.length,
            pi = 0;

        for (; pi < len && exists; pi++) {
            parent = parent[path[pi]];

            if (typeof parent === 'undefined') {
                exists = false;
            }
        }

        if (exists) {
            if (ko.isObservable(parent)) {
                parent = parent();
            }

            return parent;
        }
    }

    /**
     * Removes property from a specified object.
     * @private
     *
     * @param {Object} parent - Object from which to remove property.
     * @param {Array} path - Splitted path to the property.
     */
    function removeNested(parent, path) {
        var field = path.pop();

        parent = getNested(parent, path);

        if (_.isObject(parent)) {
            delete parent[field];
        }
    }

    return {

        /**
         * Retrieves or defines objects' property by a composite path.
         *
         * @param {Object} data - Container for the properties specified in path.
         * @param {String} path - Objects' properties divided by dots.
         * @param {*} [value] - New value for the last property.
         * @returns {*} Returns value of the last property in chain.
         *
         * @example
         *      utils.nested({}, 'one.two', 3);
         *      => { one: {two: 3} }
         */
        nested: function (data, path, value) {
            var action = arguments.length > 2 ? setNested : getNested;

            path = path ? path.split('.') : [];

            return action(data, path, value);
        },

        /**
         * Removes nested property from an object.
         *
         * @param {Object} data - Data source.
         * @param {String} path - Path to the property e.g. 'one.two.three'
         */
        nestedRemove: function (data, path) {
            path = path.split('.');

            removeNested(data, path);
        },

        /**
         * Flattens objects' nested properties.
         *
         * @param {Object} data - Object to flatten.
         * @param {String} [separator='.'] - Objects' keys separator.
         * @returns {Object} Flattened object.
         *
         * @example Example with a default separator.
         *      utils.flatten({one: { two: { three: 'value'} }});
         *      => { 'one.two.three': 'value' };
         *
         * @example Example with a custom separator.
         *      utils.flatten({one: { two: { three: 'value'} }}, '=>');
         *      => {'one=>two=>three': 'value'};
         */
        flatten: function (data, separator, parent, result) {
            separator = separator || '.';
            result = result || {};

            if (!data) {
                return result;
            }

            // UnderscoreJS each breaks when an object has a length property so we use Object.keys
            _.each(Object.keys(data), function (name) {
                var node = data[name];

                if ({}.toString.call(node) === '[object Function]') {
                    return;
                }

                if (parent) {
                    name = parent + separator + name;
                }

                typeof node === 'object' ?
                    this.flatten(node, separator, name, result) :
                    result[name] = node;

            }, this);

            return result;
        },

        /**
         * Opposite operation of the 'flatten' method.
         *
         * @param {Object} data - Previously flattened object.
         * @param {String} [separator='.'] - Keys separator.
         * @returns {Object} Object with nested properties.
         *
         * @example Example using custom separator.
         *      utils.unflatten({'one=>two': 'value'}, '=>');
         *      => {
         *          one: { two: 'value' }
         *      };
         */
        unflatten: function (data, separator) {
            var result = {};

            separator = separator || '.';

            _.each(data, function (value, nodes) {
                nodes = nodes.split(separator);

                setNested(result, nodes, value);
            });

            return result;
        },

        /**
         * Same operation as 'flatten' method,
         * but returns objects' keys wrapped in '[]'.
         *
         * @param {Object} data - Object that should be serialized.
         * @returns {Object} Serialized data.
         *
         * @example
         *      utils.serialize({one: { two: { three: 'value'} }});
         *      => { 'one[two][three]': 'value' }
         */
        serialize: function (data) {
            var result = {};

            data = this.flatten(data);

            _.each(data, function (value, keys) {
                keys = stringUtils.serializeName(keys);
                value = _.isUndefined(value) ? '' : value;

                result[keys] = value;
            }, this);

            return result;
        },

        /**
         * Performs deep extend of specified objects.
         *
         * @returns {Object|Array} Extended object.
         */
        extend: function () {
            var args = _.toArray(arguments);

            args.unshift(true);

            return $.extend.apply($, args);
        },

        /**
         * Performs a deep clone of a specified object.
         *
         * @param {(Object|Array)} data - Data that should be copied.
         * @returns {Object|Array} Cloned object.
         */
        copy: function (data) {
            var result = data,
                isArray = Array.isArray(data),
                placeholder;

            if (this.isObject(data) || isArray) {
                placeholder = isArray ? [] : {};
                result = this.extend(placeholder, data);
            }

            return result;
        },

        /**
         * Performs a deep clone of a specified object.
         * Doesn't save links to original object.
         *
         * @param {*} original - Object to clone
         * @returns {*}
         */
        hardCopy: function (original) {
            if (original === null || typeof original !== 'object') {
                return original;
            }

            return JSON.parse(JSON.stringify(original));
        },

        /**
         * Removes specified nested properties from the target object.
         *
         * @param {Object} target - Object whose properties should be removed.
         * @param {(...String|Array|Object)} list - List that specifies properties to be removed.
         * @returns {Object} Modified object.
         *
         * @example Basic usage
         *      var obj = {a: {b: 2}, c: 'a'};
         *
         *      omit(obj, 'a.b');
         *      => {'a.b': 2};
         *      obj => {a: {}, c: 'a'};
         *
         * @example Various syntaxes that would return same result
         *      omit(obj, ['a.b', 'c']);
         *      omit(obj, 'a.b', 'c');
         *      omit(obj, {'a.b': true, 'c': true});
         */
        omit: function (target, list) {
            var removed = {},
                ignored = list;

            if (this.isObject(list)) {
                ignored = [];

                _.each(list, function (value, key) {
                    if (value) {
                        ignored.push(key);
                    }
                });
            } else if (_.isString(list)) {
                ignored = _.toArray(arguments).slice(1);
            }

            _.each(ignored, function (path) {
                var value = this.nested(target, path);

                if (!_.isUndefined(value)) {
                    removed[path] = value;

                    this.nestedRemove(target, path);
                }
            }, this);

            return removed;
        },

        /**
         * Checks if provided value is a plain object.
         *
         * @param {*} value - Value to be checked.
         * @returns {Boolean}
         */
        isObject: function (value) {
            var objProto = Object.prototype;

            return typeof value == 'object' ?
            objProto.toString.call(value) === '[object Object]' :
                false;
        },

        /**
         *
         * @param {*} value
         * @returns {Boolean}
         */
        isPrimitive: function (value) {
            return value === null || ~primitives.indexOf(typeof value);
        },

        /**
         * Iterates over obj props/array elems recursively, applying action to each one
         *
         * @param {Object|Array} data - Data to be iterated.
         * @param {Function} action - Callback to be called with each item as an argument.
         * @param {Number} [maxDepth=7] - Max recursion depth.
         */
        forEachRecursive: function (data, action, maxDepth) {
            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;

            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {
                return;
            }

            if (!_.isObject(data)) {
                action(data);

                return;
            }

            _.each(data, function (value) {
                this.forEachRecursive(value, action, maxDepth);
            }, this);

            action(data);
        },

        /**
         * Maps obj props/array elems recursively
         *
         * @param {Object|Array} data - Data to be iterated.
         * @param {Function} action - Callback to transform each item.
         * @param {Number} [maxDepth=7] - Max recursion depth.
         *
         * @returns {Object|Array}
         */
        mapRecursive: function (data, action, maxDepth) {
            var newData;

            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;

            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {
                return data;
            }

            if (!_.isObject(data)) {
                return action(data);
            }

            if (_.isArray(data)) {
                newData = _.map(data, function (item) {
                    return this.mapRecursive(item, action, maxDepth);
                }, this);

                return action(newData);
            }

            newData = _.mapObject(data, function (val, key) {
                if (data.hasOwnProperty(key)) {
                    return this.mapRecursive(val, action, maxDepth);
                }

                return val;
            }, this);

            return action(newData);
        },

        /**
         * Removes empty(in common sence) obj props/array elems
         *
         * @param {*} data - Data to be cleaned.
         * @returns {*}
         */
        removeEmptyValues: function (data) {
            if (!_.isObject(data)) {
                return data;
            }

            if (_.isArray(data)) {
                return data.filter(function (item) {
                    return !this.isEmptyObj(item);
                }, this);
            }

            return _.omit(data, this.isEmptyObj.bind(this));
        },

        /**
         * Checks that argument of any type is empty in common sence:
         * empty string, string with spaces only, object without own props, empty array, null or undefined
         *
         * @param {*} val - Value to be checked.
         * @returns {Boolean}
         */
        isEmptyObj: function (val) {

            return _.isObject(val) && _.isEmpty(val) ||
            this.isEmpty(val) ||
            val && val.trim && this.isEmpty(val.trim());
        }
    };
});


/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/utils/compare',[
    'underscore',
    'mage/utils/objects'
], function (_, utils) {
    'use strict';

    var result = [];

    /**
     * Checks if all of the provided arrays contains equal values.
     *
     * @param {(Boolean|Array)} [keepOrder=false]
     * @param {Array} target
     * @returns {Boolean}
     */
    function equalArrays(keepOrder, target) {
        var args = _.toArray(arguments),
            arrays;

        if (!Array.isArray(keepOrder)) {
            arrays      = args.slice(2);
        } else {
            target      = keepOrder;
            keepOrder   = false;
            arrays      = args.slice(1);
        }

        if (!arrays.length) {
            return true;
        }

        return arrays.every(function (array) {
            if (array === target) {
                return true;
            } else if (array.length !== target.length) {
                return false;
            } else if (!keepOrder) {
                return !_.difference(target, array).length;
            }

            return array.every(function (value, index) {
                return target[index] === value;
            });
        });
    }

    /**
     * Checks if two values are different.
     *
     * @param {*} a - First value.
     * @param {*} b - Second value.
     * @returns {Boolean}
     */
    function isDifferent(a, b) {
        var oldIsPrimitive = utils.isPrimitive(a);

        if (Array.isArray(a) && Array.isArray(b)) {
            return !equalArrays(true, a, b);
        }

        return oldIsPrimitive ? a !== b : true;
    }

    /**
     * @param {String} prefix
     * @param {String} part
     */
    function getPath(prefix, part) {
        return prefix ? prefix + '.' + part : part;
    }

    /**
     * Checks if object has own specified property.
     *
     * @param {*} obj - Value to be checked.
     * @param {String} key - Key of the property.
     * @returns {Boolean}
     */
    function hasOwn(obj, key) {
        return Object.prototype.hasOwnProperty.call(obj, key);
    }

    /**
     * @param {Array} changes
     */
    function getContainers(changes) {
        var containers  = {},
            indexed     = _.indexBy(changes, 'path');

        _.each(indexed, function (change, name) {
            var path;

            name.split('.').forEach(function (part) {
                path = getPath(path, part);

                if (path in indexed) {
                    return;
                }

                (containers[path] = containers[path] || []).push(change);
            });
        });

        return containers;
    }

    /**
     * @param {String} path
     * @param {String} name
     * @param {String} type
     * @param {String} newValue
     * @param {String} oldValue
     */
    function addChange(path, name, type, newValue, oldValue) {
        var data;

        data = {
            path: path,
            name: name,
            type: type
        };

        if (type !== 'remove') {
            data.value = newValue;
            data.oldValue = oldValue;
        } else {
            data.oldValue = newValue;
        }

        result.push(data);
    }

    /**
     * @param {String} ns
     * @param {String} name
     * @param {String} type
     * @param {String} iterator
     * @param {String} placeholder
     */
    function setAll(ns, name, type, iterator, placeholder) {
        var key;

        if (arguments.length > 4) {
            type === 'add' ?
                addChange(ns, name, 'update', iterator, placeholder) :
                addChange(ns, name, 'update', placeholder, iterator);
        } else {
            addChange(ns, name, type, iterator);
        }

        if (!utils.isObject(iterator)) {
            return;
        }

        for (key in iterator) {
            if (hasOwn(iterator, key)) {
                setAll(getPath(ns, key), key, type, iterator[key]);
            }
        }
    }

    /*eslint-disable max-depth*/
    /**
     * @param {Object} old
     * @param {Object} current
     * @param {String} ns
     * @param {String} name
     */
    function compare(old, current, ns, name) {
        var key,
            oldIsObj = utils.isObject(old),
            newIsObj = utils.isObject(current);

        if (oldIsObj && newIsObj) {
            for (key in old) {
                if (hasOwn(old, key) && !hasOwn(current, key)) {
                    setAll(getPath(ns, key), key, 'remove', old[key]);
                }
            }

            for (key in current) {
                if (hasOwn(current, key)) {
                    hasOwn(old, key) ?
                        compare(old[key], current[key], getPath(ns, key), key) :
                        setAll(getPath(ns, key), key, 'add', current[key]);
                }
            }
        } else if (oldIsObj) {
            setAll(ns, name, 'remove', old, current);
        } else if (newIsObj) {
            setAll(ns, name, 'add', current, old);
        } else if (isDifferent(old, current)) {
            addChange(ns, name, 'update', current, old);
        }
    }

    /*eslint-enable max-depth*/

    return {

        /**
         *
         * @returns {Object}
         */
        compare: function () {
            var changes;

            compare.apply(null, arguments);

            changes = result.splice(0);

            return {
                containers: getContainers(changes),
                changes: changes,
                equal: !changes.length
            };
        },

        equalArrays: equalArrays
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/utils/misc',[
    'underscore',
    'jquery',
    'mage/utils/objects'
], function (_, $, utils) {
    'use strict';

    var defaultAttributes,
        ajaxSettings,
        map;

    defaultAttributes = {
        method: 'post',
        enctype: 'multipart/form-data'
    };

    ajaxSettings = {
        default: {
            method: 'POST',
            cache: false,
            processData: false,
            contentType: false
        },
        simple: {
            method: 'POST',
            dataType: 'json'
        }
    };

    map = {
        'D': 'DDD',
        'dd': 'DD',
        'd': 'D',
        'EEEE': 'dddd',
        'EEE': 'ddd',
        'e': 'd',
        'yyyy': 'YYYY',
        'yy': 'YY',
        'y': 'YYYY',
        'a': 'A'
    };

    return {

        /**
         * Generates a unique identifier.
         *
         * @param {Number} [size=7] - Length of a resulting identifier.
         * @returns {String}
         */
        uniqueid: function (size) {
            var code = Math.random() * 25 + 65 | 0,
                idstr = String.fromCharCode(code);

            size = size || 7;

            while (idstr.length < size) {
                code = Math.floor(Math.random() * 42 + 48);

                if (code < 58 || code > 64) {
                    idstr += String.fromCharCode(code);
                }
            }

            return idstr;
        },

        /**
         * Limits function call.
         *
         * @param {Object} owner
         * @param {String} target
         * @param {Number} limit
         */
        limit: function (owner, target, limit) {
            var fn = owner[target];

            owner[target] = _.debounce(fn.bind(owner), limit);
        },

        /**
         * Converts mage date format to a moment.js format.
         *
         * @param {String} mageFormat
         * @returns {String}
         */
        normalizeDate: function (mageFormat) {
            var result = mageFormat;

            _.each(map, function (moment, mage) {
                result = result.replace(
                    new RegExp(mage + '(?=([^\u0027]*\u0027[^\u0027]*\u0027)*[^\u0027]*$)'),
                    moment
                );
            });
            result = result.replace(/'(.*?)'/g, '[$1]');
            return result;
        },

        /**
         * Puts provided value in range of min and max parameters.
         *
         * @param {Number} value - Value to be located.
         * @param {Number} min - Min value.
         * @param {Number} max - Max value.
         * @returns {Number}
         */
        inRange: function (value, min, max) {
            return Math.min(Math.max(min, value), max);
        },

        /**
         * Serializes and sends data via POST request.
         *
         * @param {Object} options - Options object that consists of
         *      a 'url' and 'data' properties.
         * @param {Object} attrs - Attributes that will be added to virtual form.
         */
        submit: function (options, attrs) {
            var form        = document.createElement('form'),
                data        = utils.serialize(options.data),
                attributes  = _.extend({}, defaultAttributes, attrs || {});

            if (!attributes.action) {
                attributes.action = options.url;
            }

            data['form_key'] = window.FORM_KEY;

            _.each(attributes, function (value, name) {
                form.setAttribute(name, value);
            });

            data = _.map(
                data,
                function (value, name) {
                    return '<input type="hidden" ' +
                        'name="' + _.escape(name) + '" ' +
                        'value="' + _.escape(value) + '"' +
                        ' />';
                }
            ).join('');

            form.insertAdjacentHTML('afterbegin', data);
            document.body.appendChild(form);

            form.submit();
        },

        /**
         * Serializes and sends data via AJAX POST request.
         *
         * @param {Object} options - Options object that consists of
         *      a 'url' and 'data' properties.
         * @param {Object} config
         */
        ajaxSubmit: function (options, config) {
            var t = new Date().getTime(),
                settings;

            options.data['form_key'] = window.FORM_KEY;
            options.data = this.prepareFormData(options.data, config.ajaxSaveType);
            settings = _.extend({}, ajaxSettings[config.ajaxSaveType], options || {});

            if (!config.ignoreProcessEvents) {
                $('body').trigger('processStart');
            }

            return $.ajax(settings)
                .done(function (data) {
                    if (config.response) {
                        data.t = t;
                        config.response.data(data);
                        config.response.status(undefined);
                        config.response.status(!data.error);
                    }
                })
                .fail(function () {
                    if (config.response) {
                        config.response.status(undefined);
                        config.response.status(false);
                        config.response.data({
                            error: true,
                            messages: 'Something went wrong.',
                            t: t
                        });
                    }
                })
                .always(function () {
                    if (!config.ignoreProcessEvents) {
                        $('body').trigger('processStop');
                    }
                });
        },

        /**
         * Creates FormData object and append this data.
         *
         * @param {Object} data
         * @param {String} type
         * @returns {FormData}
         */
        prepareFormData: function (data, type) {
            var formData;

            if (type === 'default') {
                formData = new FormData();
                _.each(utils.serialize(data), function (val, name) {
                    formData.append(name, val);
                });
            } else if (type === 'simple') {
                formData = utils.serialize(data);
            }

            return formData;
        },

        /**
         * Filters data object. Finds properties with suffix
         * and sets their values to properties with the same name without suffix.
         *
         * @param {Object} data - The data object that should be filtered
         * @param {String} suffix - The string by which data object should be filtered
         * @param {String} separator - The string that is separator between property and suffix
         *
         * @returns {Object} Filtered data object
         */
        filterFormData: function (data, suffix, separator) {
            data = data || {};
            suffix = suffix || 'prepared-for-send';
            separator = separator || '-';

            _.each(data, function (value, key) {
                if (_.isObject(value) && !Array.isArray(value)) {
                    this.filterFormData(value, suffix, separator);
                } else if (_.isString(key) && ~key.indexOf(suffix)) {
                    data[key.split(separator)[0]] = value;
                    delete data[key];
                }
            }, this);

            return data;
        },

        /**
         * Replaces special characters with their corresponding HTML entities.
         *
         * @param {String} string - Text to escape.
         * @returns {String} Escaped text.
         */
        escape: function (string) {
            return string ? $('<p></p>').text(string).html().replace(/"/g, '&quot;') : string;
        },

        /**
         * Replaces symbol codes with their unescaped counterparts.
         *
         * @param {String} data
         *
         * @returns {String}
         */
        unescape: function (data) {
            var unescaped = _.unescape(data),
                mapCharacters = {
                    '&#039;': '\''
                };

            _.each(mapCharacters, function (value, key) {
                unescaped = unescaped.replace(key, value);
            });

            return unescaped;
        },

        /**
         * Converts PHP IntlFormatter format to moment format.
         *
         * @param {String} format - PHP format
         * @returns {String} - moment compatible formatting
         */
        convertToMomentFormat: function (format) {
            var newFormat;

            newFormat = format.replace(/yyyy|yy|y/, 'YYYY'); // replace the year
            newFormat = newFormat.replace(/dd|d/g, 'DD'); // replace the date

            return newFormat;
        },

        /**
         * Get Url Parameters.
         *
         * @param {String} url - Url string
         * @returns {Object}
         */
        getUrlParameters: function (url) {
            var params = {},
                queries = url.split('?'),
                temp,
                i,
                l;

            if (!queries[1]) {
                return params;
            }

            queries = queries[1].split('&');

            for (i = 0, l = queries.length; i < l; i++) {
                temp = queries[i].split('=');

                if (temp[1]) {
                    params[temp[0]] = decodeURIComponent(temp[1].replace(/\+/g, '%20'));
                } else {
                    params[temp[0]] = '';
                }
            }

            return params;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/* eslint-disable no-shadow */

define('mage/utils/template',[
    'jquery',
    'underscore',
    'mage/utils/objects',
    'mage/utils/strings'
], function ($, _, utils, stringUtils) {
    'use strict';

    var tmplSettings = _.templateSettings,
        interpolate = /\$\{([\s\S]+?)\}/g,
        opener = '${',
        template,
        hasStringTmpls;

    /**
     * Identifies whether ES6 templates are supported.
     */
    hasStringTmpls = (function () {
        var testString = 'var foo = "bar"; return `${ foo }` === foo';

        try {
            return Function(testString)();
        } catch (e) {
            return false;
        }
    })();

    /**
     * Objects can specify how to use templating for their properties - getting that configuration.
     *
     * To disable rendering for all properties of your object add __disableTmpl: true.
     * To disable for specific property add __disableTmpl: {propertyName: true}.
     * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}.
     *
     * @param {String} tmpl
     * @param {Object | undefined} target
     * @returns {Boolean|Object}
     */
    function isTmplIgnored(tmpl, target) {
        var parsedTmpl;

        try {
            parsedTmpl = JSON.parse(tmpl);

            if (typeof parsedTmpl === 'object') {
                return tmpl.includes('__disableTmpl');
            }
        } catch (e) {
        }

        if (typeof target !== 'undefined') {
            if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) {
                return target.__disableTmpl;
            }
        }

        return false;

    }

    if (hasStringTmpls) {

        /*eslint-disable no-unused-vars, no-eval*/
        /**
         * Evaluates template string using ES6 templates.
         *
         * @param {String} tmpl - Template string.
         * @param {Object} $ - Data object used in a template.
         * @returns {String} Compiled template.
         */
        template = function (tmpl, $) {
            return eval('`' + tmpl + '`');
        };

        /*eslint-enable no-unused-vars, no-eval*/
    } else {

        /**
         * Fallback function used when ES6 templates are not supported.
         * Uses underscore templates renderer.
         *
         * @param {String} tmpl - Template string.
         * @param {Object} data - Data object used in a template.
         * @returns {String} Compiled template.
         */
        template = function (tmpl, data) {
            var cached = tmplSettings.interpolate;

            tmplSettings.interpolate = interpolate;

            tmpl = _.template(tmpl, {
                variable: '$'
            })(data);

            tmplSettings.interpolate = cached;

            return tmpl;
        };
    }

    /**
     * Checks if provided value contains template syntax.
     *
     * @param {*} value - Value to be checked.
     * @returns {Boolean}
     */
    function isTemplate(value) {
        return typeof value === 'string' &&
            value.indexOf(opener) !== -1 &&
            // the below pattern almost always indicates an accident which should not cause template evaluation
            // refuse to evaluate
            value.indexOf('${{') === -1;
    }

    /**
     * Iteratively processes provided string
     * until no templates syntax will be found.
     *
     * @param {String} tmpl - Template string.
     * @param {Object} data - Data object used in a template.
     * @param {Boolean} [castString=false] - Flag that indicates whether template
     *      should be casted after evaluation to a value of another type or
     *      that it should be leaved as a string.
     * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0.
     * @returns {*} Compiled template.
     */
    function render(tmpl, data, castString, maxCycles) {
        var last = tmpl,
            cycles = 0;

        while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) {
            if (!isTmplIgnored(tmpl)) {
                tmpl = template(tmpl, data);
            }

            if (tmpl === last) {
                break;
            }

            last = tmpl;
            cycles++;
        }

        return castString ?
            stringUtils.castString(tmpl) :
            tmpl;
    }

    return {

        /**
         * Applies provided data to the template.
         *
         * @param {Object|String} tmpl
         * @param {Object} [data] - Data object to match with template.
         * @param {Boolean} [castString=false] - Flag that indicates whether template
         *      should be casted after evaluation to a value of another type or
         *      that it should be leaved as a string.
         * @returns {*}
         *
         * @example Template defined as a string.
         *      var source = { foo: 'Random Stuff', bar: 'Some' };
         *
         *      utils.template('${ $.bar } ${ $.foo }', source);
         *      => 'Some Random Stuff';
         *
         * @example Template defined as an object.
         *      var tmpl = {
         *              key: {'${ $.$data.bar }': '${ $.$data.foo }'},
         *              foo: 'bar',
         *              x1: 2, x2: 5,
         *              delta: '${ $.x2 - $.x1 }',
         *              baz: 'Upper ${ $.foo.toUpperCase() }'
         *      };
         *
         *      utils.template(tmpl, source);
         *      => {
         *          key: {'Some': 'Random Stuff'},
         *          foo: 'bar',
         *          x1: 2, x2: 5,
         *          delta: 3,
         *          baz: 'Upper BAR'
         *      };
         */
        template: function (tmpl, data, castString, dontClone) {
            if (typeof tmpl === 'string') {
                return render(tmpl, data, castString);
            }

            if (!dontClone) {
                tmpl = utils.copy(tmpl);
            }

            tmpl.$data = data || {};

            /**
             * Template iterator function.
             */
            _.each(tmpl, function iterate(value, key, list) {
                var disabled,
                    maxCycles;

                if (key === '$data') {
                    return;
                }

                if (isTemplate(key)) {
                    delete list[key];

                    key = render(key, tmpl);
                    list[key] = value;
                }

                if (isTemplate(value)) {
                    //Getting template disabling settings, can be true for all disabled and separate settings
                    //for each property.
                    disabled = isTmplIgnored(value, list);

                    if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) {
                        //Checking if specific settings for a property provided.
                        maxCycles = disabled[key];
                    }

                    if (disabled === true || maxCycles === true) {
                        //Rendering for all properties is disabled.
                        maxCycles = 0;
                    }

                    list[key] = render(value, tmpl, castString, maxCycles);
                } else if ($.isPlainObject(value) || Array.isArray(value)) {
                    _.each(value, iterate);
                }
            });

            delete tmpl.$data;

            return tmpl;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/utils/main',['require','underscore','./arrays','./compare','./misc','./objects','./strings','./template'],function (require) {
    'use strict';

    var utils = {},
        _ = require('underscore'),
        root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            Function('return this')() || {};

    root._ = _;

    return _.extend(
        utils,
        require('./arrays'),
        require('./compare'),
        require('./misc'),
        require('./objects'),
        require('./strings'),
        require('./template')
    );
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/core/events',[
    'ko',
    'underscore'
], function (ko, _) {
    'use strict';

    var eventsMap = new WeakMap();

    /**
     * Returns events map or a specific event
     * data associated with a provided object.
     *
     * @param {Object} obj - Key in the events weakmap.
     * @param {String} [name] - Name of the event.
     * @returns {Map|Array|Boolean}
     */
    function getEvents(obj, name) {
        var events = eventsMap.get(obj);

        if (!events) {
            return false;
        }

        return name ? events.get(name) : events;
    }

    /**
     * Adds new event handler.
     *
     * @param {Object} obj - Key in the events weakmap.
     * @param {String} ns - Callback namespace.
     * @param {Function} callback - Event callback.
     * @param {String} name - Name of the event.
     */
    function addHandler(obj, ns, callback, name) {
        var events      = getEvents(obj),
            observable,
            data;

        observable = !ko.isObservable(obj[name]) ?
            ko.getObservable(obj, name) :
            obj[name];

        if (observable) {
            observable.subscribe(callback);

            return;
        }

        if (!events) {
            events = new Map();

            eventsMap.set(obj, events);
        }

        data = {
            callback: callback,
            ns: ns
        };

        // Vaimo patch: allow KO observable to be skipped to force event to trigger even when no changes
        // encountered. Having exclamation mark in the end of the event name bypasses the ko.getObservable
        // part of this script.
        if (name.substr(-1) === '!' && name.length > 1) {
            name = name.substr(0, name.length - 1);
        }

        events.has(name) ?
            events.get(name).push(data) :
            events.set(name, [data]);
    }

    /**
     * Invokes provided callbacks with a specified arguments.
     *
     * @param {Array} handlers
     * @param {Array} args
     * @returns {Boolean}
     */
    function trigger(handlers, args) {
        var bubble = true,
            callback;

        handlers.forEach(function (handler) {
            callback = handler.callback;

            if (callback.apply(null, args) === false) {
                bubble = false;
            }
        });

        return bubble;
    }

    return {

        /**
         * Calls callback when name event is triggered.
         * @param  {String}   events
         * @param  {Function} callback
         * @param  {Function} ns
         * @return {Object} reference to this
         */
        on: function (events, callback, ns) {
            var iterator;

            if (arguments.length < 2) {
                ns = callback;
            }

            iterator = addHandler.bind(null, this, ns);

            _.isObject(events) ?
                _.each(events, iterator) :
                iterator(callback, events);

            return this;
        },

        /**
         * Removed callback from listening to target event
         * @param  {String} ns
         * @return {Object} reference to this
         */
        off: function (ns) {
            var storage = getEvents(this);

            if (!storage) {
                return this;
            }

            storage.forEach(function (handlers, name) {
                handlers = handlers.filter(function (handler) {
                    return !ns ? false : handler.ns !== ns;
                });

                handlers.length ?
                    storage.set(name, handlers) :
                    storage.delete(name);
            });

            return this;
        },

        /**
         * Triggers event and executes all attached callbacks.
         *
         * @param {String} name - Name of the event to be triggered.
         * @returns {Boolean}
         */
        trigger: function (name) {
            var handlers,
                args;

            handlers = getEvents(this, name),
            args = _.toArray(arguments).slice(1);

            if (!handlers || !name) {
                return true;
            }

            return trigger(handlers, args);
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * Utility methods used to wrap and extend functions.
 *
 * @example Usage of a 'wrap' method with arguments delegation.
 *      var multiply = function (a, b) {
 *          return a * b;
 *      };
 *
 *      multiply = module.wrap(multiply, function (orig) {
 *          return 'Result is: ' + orig();
 *      });
 *
 *      multiply(2, 2);
 *      => 'Result is: 4'
 *
 * @example Usage of 'wrapSuper' method.
 *      var multiply = function (a, b) {
 *         return a * b;
 *      };
 *
 *      var obj = {
 *          multiply: module.wrapSuper(multiply, function () {
 *              return 'Result is: ' + this._super();
 *          });
 *      };
 *
 *      obj.multiply(2, 2);
 *      => 'Result is: 4'
 */
define('mage/utils/wrapper',[
    'underscore'
], function (_) {
    'use strict';

    /**
     * Checks if string has a '_super' substring.
     */
    var superReg = /\b_super\b/;

    return {

        /**
         * Wraps target function with a specified wrapper, which will receive
         * reference to the original function as a first argument.
         *
         * @param {Function} target - Function to be wrapped.
         * @param {Function} wrapper - Wrapper function.
         * @returns {Function} Wrapper function.
         */
        wrap: function (target, wrapper) {
            if (!_.isFunction(target) || !_.isFunction(wrapper)) {
                return wrapper;
            }

            return function () {
                var args    = _.toArray(arguments),
                    ctx     = this,
                    _super;

                /**
                 * Function that will be passed to the wrapper.
                 * If no arguments will be passed to it, then the original
                 * function will be called with an arguments of a wrapper function.
                 */
                _super = function () {
                    var superArgs = arguments.length ? arguments : args.slice(1);

                    return target.apply(ctx, superArgs);
                };

                args.unshift(_super);

                return wrapper.apply(ctx, args);
            };
        },

        /**
         * Wraps the incoming function to implement support of the '_super' method.
         *
         * @param {Function} target - Function to be wrapped.
         * @param {Function} wrapper - Wrapper function.
         * @returns {Function} Wrapped function.
         */
        wrapSuper: function (target, wrapper) {
            if (!this.hasSuper(wrapper) || !_.isFunction(target)) {
                return wrapper;
            }

            return function () {
                var _super  = this._super,
                    args    = arguments,
                    result;

                /**
                 * Temporary define '_super' method which
                 * contains call to the original function.
                 */
                this._super = function () {
                    var superArgs = arguments.length ? arguments : args;

                    return target.apply(this, superArgs);
                };

                result = wrapper.apply(this, args);

                this._super = _super;

                return result;
            };
        },

        /**
         * Checks wether the incoming method contains calls of the '_super' method.
         *
         * @param {Function} fn - Function to be checked.
         * @returns {Boolean}
         */
        hasSuper: function (fn) {
            return _.isFunction(fn) && superReg.test(fn);
        },

        /**
         * Extends target object with provided extenders.
         * If property in target and extender objects is a function,
         * then it will be wrapped using 'wrap' method.
         *
         * @param {Object} target - Object to be extended.
         * @param {...Object} extenders - Multiple extenders objects.
         * @returns {Object} Modified target object.
         */
        extend: function (target) {
            var extenders = _.toArray(arguments).slice(1),
                iterator = this._extend.bind(this, target);

            extenders.forEach(iterator);

            return target;
        },

        /**
         * Same as the 'extend' method, but operates only on one extender object.
         *
         * @private
         * @param {Object} target
         * @param {Object} extender
         */
        _extend: function (target, extender) {
            _.each(extender, function (value, key) {
                target[key] = this.wrap(target[key], extender[key]);
            }, this);
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/core/class',[
    'underscore',
    'mageUtils',
    'mage/utils/wrapper'
], function (_, utils, wrapper) {
    'use strict';

    var Class;

    /**
     * Returns property of an object if
     * it's his own property.
     *
     * @param {Object} obj - Object whose property should be retrieved.
     * @param {String} prop - Name of the property.
     * @returns {*} Value of the property or false.
     */
    function getOwn(obj, prop) {
        return _.isObject(obj) && obj.hasOwnProperty(prop) && obj[prop];
    }

    /**
     * Creates constructor function which allows
     * initialization without usage of a 'new' operator.
     *
     * @param {Object} protoProps - Prototypal properties of a new constructor.
     * @param {Function} constructor
     * @returns {Function} Created constructor.
     */
    function createConstructor(protoProps, constructor) {
        var UiClass = constructor;

        if (!UiClass) {

            /**
             * Default constructor function.
             */
            UiClass = function () {
                var obj = this;

                if (!_.isObject(obj) || Object.getPrototypeOf(obj) !== UiClass.prototype) {
                    obj = Object.create(UiClass.prototype);
                }

                obj.initialize.apply(obj, arguments);

                return obj;
            };
        }

        UiClass.prototype = protoProps;
        UiClass.prototype.constructor = UiClass;

        return UiClass;
    }

    Class = createConstructor({

        /**
         * Entry point to the initialization of constructor's instance.
         *
         * @param {Object} [options={}]
         * @returns {Class} Chainable.
         */
        initialize: function (options) {
            this.initConfig(options);

            return this;
        },

        /**
         * Recursively extends data specified in constructors' 'defaults'
         * property with provided options object. Evaluates resulting
         * object using string templates (see: mage/utils/template.js).
         *
         * @param {Object} [options={}]
         * @returns {Class} Chainable.
         */
        initConfig: function (options) {
            var defaults    = this.constructor.defaults,
                config      = utils.extend({}, defaults, options || {}),
                ignored     = config.ignoreTmpls || {},
                cached      = utils.omit(config, ignored);

            config = utils.template(config, this, false, true);

            _.each(cached, function (value, key) {
                utils.nested(config, key, value);
            });

            return _.extend(this, config);
        }
    });

    _.extend(Class, {
        defaults: {
            ignoreTmpls: {
                templates: true
            }
        },

        /**
         * Creates new constructor based on a current prototype properties,
         * extending them with properties specified in 'exender' object.
         *
         * @param {Object} [extender={}]
         * @returns {Function} New constructor.
         */
        extend: function (extender) {
            var parent      = this,
                parentProto = parent.prototype,
                childProto  = Object.create(parentProto),
                child       = createConstructor(childProto, getOwn(extender, 'constructor')),
                defaults;

            extender = extender || {};
            defaults = extender.defaults;

            delete extender.defaults;

            _.each(extender, function (method, name) {
                childProto[name] = wrapper.wrapSuper(parentProto[name], method);
            });

            child.defaults = utils.extend({}, parent.defaults || {});

            if (defaults) {
                utils.extend(child.defaults, defaults);
                extender.defaults = defaults;
            }

            return _.extend(child, {
                __super__:  parentProto,
                extend:     parent.extend
            });
        }
    });

    return Class;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/core/element/links',[
    'ko',
    'underscore',
    'mageUtils',
    'uiRegistry'
], function (ko, _, utils, registry) {
    'use strict';

    /**
     * Parse provided data.
     *
     * @param {String} placeholder
     * @param {String} data
     * @param {String} direction
     * @returns {Boolean|Object}
     */
    function parseData(placeholder, data, direction) {
        if (typeof data !== 'string') {
            return false;
        }

        data = data.split(':');

        if (!data[0]) {
            return false;
        }

        if (!data[1]) {
            data[1] = data[0];
            data[0] = placeholder;
        }

        return {
            target: data[0],
            property: data[1],
            direction: direction
        };
    }

    /**
     * Check if value not empty.
     *
     * @param {*} value
     * @returns {Boolean}
     */
    function notEmpty(value) {
        return typeof value !== 'undefined' && value != null;
    }

    /**
     * Update value for linked component.
     *
     * @param {Object} data
     * @param {Object} owner
     * @param {Object} target
     * @param {*} value
     */
    function updateValue(data, owner, target, value) {
        var component = target.component,
            property = target.property,
            linked = data.linked;

        if (data.mute) {
            return;
        }

        if (linked) {
            linked.mute = true;
        }

        if (owner.component !== target.component) {
            value = data.inversionValue ? !utils.copy(value) : utils.copy(value);
        }

        component.set(property, value, owner);

        if (property === 'disabled' && value) {
            component.set('validate', value, owner);
        }

        if (linked) {
            linked.mute = false;
        }
    }

    /**
     * Get value form owner component property.
     *
     * @param {Object} owner
     * @returns {*}
     */
    function getValue(owner) {
        var component = owner.component,
            property = owner.property;

        return component.get(property);
    }

    /**
     * Format provided params to object.
     *
     * @param {String} ownerComponent
     * @param {String} targetComponent
     * @param {String} ownerProp
     * @param {String} targetProp
     * @param {String} direction
     * @returns {Object}
     */
    function form(ownerComponent, targetComponent, ownerProp, targetProp, direction) {
        var result,
            tmp;

        result = {
            owner: {
                component: ownerComponent,
                property: ownerProp
            },
            target: {
                component: targetComponent,
                property: targetProp
            }
        };

        if (direction === 'exports') {
            tmp = result.owner;
            result.owner = result.target;
            result.target = tmp;
        }

        return result;
    }

    /**
     * Set data to linked property.
     *
     * @param {Object} map
     * @param {Object} data
     */
    function setLinked(map, data) {
        var match;

        if (!map) {
            return;
        }

        match = _.findWhere(map, {
            linked: false,
            target: data.target,
            property: data.property
        });

        if (match) {
            match.linked = data;
            data.linked = match;
        }
    }

    /**
     * Set data by direction.
     *
     * @param {Object} maps
     * @param {String} property
     * @param {Object} data
     */
    function setData(maps, property, data) {
        var direction   = data.direction,
            map         = maps[direction];

        data.linked = false;

        (map[property] = map[property] || []).push(data);

        direction = direction === 'imports' ? 'exports' : 'imports';

        setLinked(maps[direction][property], data);
    }

    /**
     * Set links for components.
     *
     * @param {String} target
     * @param {String} owner
     * @param {Object} data
     * @param {String} property
     * @param {Boolean} immediate
     */
    function setLink(target, owner, data, property, immediate) {
        var direction = data.direction,
            formated = form(target, owner, data.property, property, direction),
            callback,
            value;

        owner = formated.owner;
        target = formated.target;

        callback = updateValue.bind(null, data, owner, target);

        owner.component.on(owner.property, callback, target.component.name);

        if (immediate) {
            value = getValue(owner);

            if (notEmpty(value)) {
                updateValue(data, owner, target, value);
            }
        }
    }

    /**
     * Transfer data between components.
     *
     * @param {Object} owner
     * @param {Object} data
     */
    function transfer(owner, data) {
        var args = _.toArray(arguments);

        if (data.target.substr(0, 1) === '!') {
            data.target = data.target.substr(1);
            data.inversionValue = true;
        }

        if (owner.name === data.target) {
            args.unshift(owner);

            setLink.apply(null, args);
        } else {
            registry.get(data.target, function (target) {
                args.unshift(target);

                setLink.apply(null, args);
            });
        }
    }

    return {
        /**
         * Assign listeners.
         *
         * @param {Object} listeners
         * @returns {Object} Chainable
         */
        setListeners: function (listeners) {
            var owner = this,
                data;

            _.each(listeners, function (callbacks, sources) {
                sources = sources.split(' ');
                callbacks = callbacks.split(' ');

                sources.forEach(function (target) {
                    callbacks.forEach(function (callback) {//eslint-disable-line max-nested-callbacks
                        data = parseData(owner.name, target, 'imports');

                        if (data) {
                            setData(owner.maps, callback, data);
                            transfer(owner, data, callback);
                        }
                    });
                });
            });

            return this;
        },

        /**
         * Set links in provided direction.
         *
         * @param {Object} links
         * @param {String} direction
         * @returns {Object} Chainable
         */
        setLinks: function (links, direction) {
            var owner = this,
                property,
                data;

            for (property in links) {
                if (links.hasOwnProperty(property)) {
                    data = parseData(owner.name, links[property], direction);

                    if (data) {//eslint-disable-line max-depth
                        setData(owner.maps, property, data);
                        transfer(owner, data, property, true);
                    }
                }
            }

            return this;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/core/storage/local',[
    'underscore',
    'uiRegistry',
    'mageUtils',
    'uiEvents'
], function (_, registry, utils, EventsBus) {
    'use strict';

    var root = 'appData',
        localStorage,
        hasSupport,
        storage;

    /**
     * Flag which indicates whether localStorage is supported.
     */
    hasSupport = (function () {
        var key = '_storageSupported';

        try {
            localStorage = window.localStorage;
            localStorage.setItem(key, 'true');

            if (localStorage.getItem(key) === 'true') {
                localStorage.removeItem(key);

                return true;
            }

            return false;
        } catch (e) {
            return false;
        }
    })();

    if (!hasSupport) {
        localStorage = {
            _data: {},

            /**
             * Sets value of the specified item.
             *
             * @param {String} key - Key of the property.
             * @param {*} value - Properties' value.
             */
            setItem: function (key, value) {
                this._data[key] = value + '';
            },

            /**
             * Retrieves specified item.
             *
             * @param {String} key - Key of the property to be retrieved.
             */
            getItem: function (key) {
                return this._data[key];
            },

            /**
             * Removes specified item.
             *
             * @param {String} key - Key of the property to be removed.
             */
            removeItem: function (key) {
                delete this._data[key];
            },

            /**
             * Removes all items.
             */
            clear: function () {
                this._data = {};
            }
        };
    }

    /**
     * Extracts and parses data stored in localStorage by the
     * key specified in 'root' variable.
     *
     * @returns {Object}
     */
    function getRoot() {
        var data = localStorage.getItem(root),
            result = {};

        if (!_.isNull(data) && typeof data != 'undefined') {
            result = JSON.parse(data);
        }

        return result;
    }

    /**
     * Writes provided data to the localStorage.
     *
     * @param {*} data - Data to be stored.
     */
    function setRoot(data) {
        localStorage.setItem(root, JSON.stringify(data));
    }

    /**
     * Provides methods to work with a localStorage
     * as a single nested structure.
     */
    storage = _.extend({

        /**
         * Retrieves value of the specified property.
         *
         * @param {String} path - Path to the property.
         *
         * @example Retrieving data.
         *      localStorage =>
         *          'appData' => '
         *              "one": {"two": "three"}
         *          '
         *      storage.get('one.two')
         *      => "three"
         *
         *      storage.get('one')
         *      => {"two": "three"}
         */
        get: function (path) {
            var data = getRoot();

            return utils.nested(data, path);
        },

        /**
         * Sets specified data to the localStorage.
         *
         * @param {String} path - Path of the property.
         * @param {*} value - Value of the property.
         *
         * @example Setting data.
         *      storage.set('one.two', 'four');
         *      => localStorage =>
         *          'appData' => '
         *              "one": {"two": "four"}
         *          '
         */
        set: function (path, value) {
            var data = getRoot();

            utils.nested(data, path, value);

            setRoot(data);
        },

        /**
         * Removes specified data from the localStorage.
         *
         * @param {String} path - Path to the property that should be removed.
         *
         * @example Removing data.
         *      storage.remove('one.two', 'four');
         *      => localStorage =>
         *          'appData' => '
         *              "one": {}
         *          '
         */
        remove: function (path) {
            var data = getRoot();

            utils.nestedRemove(data, path);

            setRoot(data);
        }
    }, EventsBus);

    registry.set('localStorage', storage);

    return storage;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/lib/core/element/element',[
    'ko',
    'underscore',
    'mageUtils',
    'uiRegistry',
    'uiEvents',
    'uiClass',
    './links',
    '../storage/local'
], function (ko, _, utils, registry, Events, Class, links) {
    'use strict';

    var Element;

    /**
     * Creates observable property using knockouts'
     * 'observableArray' or 'observable' methods,
     * depending on a type of 'value' parameter.
     *
     * @param {Object} obj - Object to whom property belongs.
     * @param {String} key - Key of the property.
     * @param {*} value - Initial value.
     */
    function observable(obj, key, value) {
        var method = Array.isArray(value) ? 'observableArray' : 'observable';

        if (_.isFunction(obj[key]) && !ko.isObservable(obj[key])) {
            return;
        }

        if (ko.isObservable(value)) {
            value = value();
        }

        ko.isObservable(obj[key]) ?
            obj[key](value) :
            obj[key] = ko[method](value);
    }

    /**
     * Creates observable property using 'track' method.
     *
     * @param {Object} obj - Object to whom property belongs.
     * @param {String} key - Key of the property.
     * @param {*} value - Initial value.
     */
    function accessor(obj, key, value) {
        if (_.isFunction(obj[key]) || ko.isObservable(obj[key])) {
            return;
        }

        obj[key] = value;

        if (!ko.es5.isTracked(obj, key)) {
            ko.track(obj, [key]);
        }
    }

    Element = _.extend({
        defaults: {
            _requested: {},
            containers: [],
            exports: {},
            imports: {},
            links: {},
            listens: {},
            name: '',
            ns: '${ $.name.split(".")[0] }',
            provider: '',
            registerNodes: true,
            source: null,
            statefull: {},
            template: '',
            tracks: {},
            storageConfig: {
                provider: 'localStorage',
                namespace: '${ $.name }',
                path: '${ $.storageConfig.provider }:${ $.storageConfig.namespace }'
            },
            maps: {
                imports: {},
                exports: {}
            },
            modules: {
                storage: '${ $.storageConfig.provider }'
            }
        },

        /**
         * Initializes model instance.
         *
         * @returns {Element} Chainable.
         */
        initialize: function () {
            this._super()
                .initObservable()
                .initModules()
                .initStatefull()
                .initLinks()
                .initUnique();

            return this;
        },

        /**
         * Initializes observable properties.
         *
         * @returns {Element} Chainable.
         */
        initObservable: function () {
            _.each(this.tracks, function (enabled, key) {
                if (enabled) {
                    this.track(key);
                }
            }, this);

            return this;
        },

        /**
         * Parses 'modules' object and creates
         * async wrappers for specified components.
         *
         * @returns {Element} Chainable.
         */
        initModules: function () {
            _.each(this.modules, function (name, property) {
                if (name) {
                    this[property] = this.requestModule(name);
                }
            }, this);

            if (!_.isFunction(this.source)) {
                this.source = registry.get(this.provider);
            }

            return this;
        },

        /**
         * Called when current element was injected to another component.
         *
         * @param {Object} parent - Instance of a 'parent' component.
         * @returns {Collection} Chainable.
         */
        initContainer: function (parent) {
            this.containers.push(parent);

            return this;
        },

        /**
         * Initializes statefull properties
         * based on the keys of 'statefull' object.
         *
         * @returns {Element} Chainable.
         */
        initStatefull: function () {
            _.each(this.statefull, function (path, key) {
                if (path) {
                    this.setStatefull(key, path);
                }
            }, this);

            return this;
        },

        /**
         * Initializes links between properties.
         *
         * @returns {Element} Chainbale.
         */
        initLinks: function () {
            return this.setListeners(this.listens)
                       .setLinks(this.links, 'imports')
                       .setLinks(this.links, 'exports')
                       .setLinks(this.exports, 'exports')
                       .setLinks(this.imports, 'imports');
        },

        /**
         * Initializes listeners of the unique property.
         *
         * @returns {Element} Chainable.
         */
        initUnique: function () {
            var update = this.onUniqueUpdate.bind(this),
                uniqueNs = this.uniqueNs;

            this.hasUnique = this.uniqueProp && uniqueNs;

            if (this.hasUnique) {
                this.source.on(uniqueNs, update, this.name);
            }

            return this;
        },

        /**
         * Makes specified property to be stored automatically.
         *
         * @param {String} key - Name of the property
         *      that will be stored.
         * @param {String} [path=key] - Path to the property in storage.
         * @returns {Element} Chainable.
         */
        setStatefull: function (key, path) {
            var link = {};

            path        = !_.isString(path) || !path ? key : path;
            link[key]   = this.storageConfig.path + '.' + path;

            this.setLinks(link, 'imports')
                .setLinks(link, 'exports');

            return this;
        },

        /**
         * Updates property specified in uniqueNs
         * if elements' unique property is set to 'true'.
         *
         * @returns {Element} Chainable.
         */
        setUnique: function () {
            var property = this.uniqueProp;

            if (this[property]()) {
                this.source.set(this.uniqueNs, this.name);
            }

            return this;
        },

        /**
         * Creates 'async' wrapper for the specified component
         * using uiRegistry 'async' method and caches it
         * in a '_requested' components  object.
         *
         * @param {String} name - Name of requested component.
         * @returns {Function} Async module wrapper.
         */
        requestModule: function (name) {
            var requested = this._requested;

            if (!requested[name]) {
                requested[name] = registry.async(name);
            }

            return requested[name];
        },

        /**
         * Returns path to elements' template.
         *
         * @returns {String}
         */
        getTemplate: function () {
            return this.template;
        },

        /**
         * Checks if template was specified for an element.
         *
         * @returns {Boolean}
         */
        hasTemplate: function () {
            return !!this.template;
        },

        /**
         * Returns value of the nested property.
         *
         * @param {String} path - Path to the property.
         * @returns {*} Value of the property.
         */
        get: function (path) {
            return utils.nested(this, path);
        },

        /**
         * Sets provided value as a value of the specified nested property.
         * Triggers changes notifications, if value has mutated.
         *
         * @param {String} path - Path to property.
         * @param {*} value - New value of the property.
         * @returns {Element} Chainable.
         */
        set: function (path, value) {
            var data = this.get(path),
                diffs;

            diffs = !_.isFunction(data) && !this.isTracked(path) ?
                utils.compare(data, value, path) :
                false;

            utils.nested(this, path, value);

            if (diffs) {
                this._notifyChanges(diffs);
            }

            return this;
        },

        /**
         * Removes nested property from the object.
         *
         * @param {String} path - Path to the property.
         * @returns {Element} Chainable.
         */
        remove: function (path) {
            var data = utils.nested(this, path),
                diffs;

            if (_.isUndefined(data) || _.isFunction(data)) {
                return this;
            }

            diffs = utils.compare(data, undefined, path);

            utils.nestedRemove(this, path);

            this._notifyChanges(diffs);

            return this;
        },

        /**
         * Creates observable properties for the current object.
         *
         * If 'useTrack' flag is set to 'true' then each property will be
         * created with a ES5 get/set accessor descriptors, instead of
         * making them an observable functions.
         * See 'knockout-es5' library for more information.
         *
         * @param {Boolean} [useAccessors=false] - Whether to create an
         *      observable function or to use property accesessors.
         * @param {(Object|String|Array)} properties - List of observable properties.
         * @returns {Element} Chainable.
         *
         * @example Sample declaration and equivalent knockout methods.
         *      this.key = 'value';
         *      this.array = ['value'];
         *
         *      this.observe(['key', 'array']);
         *      =>
         *          this.key = ko.observable('value');
         *          this.array = ko.observableArray(['value']);
         *
         * @example Another syntaxes of the previous example.
         *      this.observe({
         *          key: 'value',
         *          array: ['value']
         *      });
         */
        observe: function (useAccessors, properties) {
            var model = this,
                trackMethod;

            if (typeof useAccessors !== 'boolean') {
                properties   = useAccessors;
                useAccessors = false;
            }

            trackMethod = useAccessors ? accessor : observable;

            if (_.isString(properties)) {
                properties = properties.split(' ');
            }

            if (Array.isArray(properties)) {
                properties.forEach(function (key) {
                    trackMethod(model, key, model[key]);
                });
            } else if (typeof properties === 'object') {
                _.each(properties, function (value, key) {
                    trackMethod(model, key, value);
                });
            }

            return this;
        },

        /**
         * Delegates call to 'observe' method but
         * with a predefined 'useAccessors' flag.
         *
         * @param {(String|Array|Object)} properties - List of observable properties.
         * @returns {Element} Chainable.
         */
        track: function (properties) {
            this.observe(true, properties);

            return this;
        },

        /**
         * Checks if specified property is tracked.
         *
         * @param {String} property - Property to be checked.
         * @returns {Boolean}
         */
        isTracked: function (property) {
            return ko.es5.isTracked(this, property);
        },

        /**
         * Invokes subscribers for the provided changes.
         *
         * @param {Object} diffs - Object with changes descriptions.
         * @returns {Element} Chainable.
         */
        _notifyChanges: function (diffs) {
            diffs.changes.forEach(function (change) {
                this.trigger(change.path, change.value, change);
            }, this);

            _.each(diffs.containers, function (changes, name) {
                var value = utils.nested(this, name);

                this.trigger(name, value, changes);
            }, this);

            return this;
        },

        /**
         * Extracts all stored data and sets it to element.
         *
         * @returns {Element} Chainable.
         */
        restore: function () {
            var ns = this.storageConfig.namespace,
                storage = this.storage();

            if (storage) {
                utils.extend(this, storage.get(ns));
            }

            return this;
        },

        /**
         * Stores value of the specified property in components' storage module.
         *
         * @param {String} property
         * @param {*} [data=this[property]]
         * @returns {Element} Chainable.
         */
        store: function (property, data) {
            var ns = this.storageConfig.namespace,
                path = utils.fullPath(ns, property);

            if (arguments.length < 2) {
                data = this.get(property);
            }

            this.storage('set', path, data);

            return this;
        },

        /**
         * Extracts specified property from storage.
         *
         * @param {String} [property] - Name of the property
         *      to be extracted. If not specified then all of the
         *      stored will be returned.
         * @returns {*}
         */
        getStored: function (property) {
            var ns = this.storageConfig.namespace,
                path = utils.fullPath(ns, property),
                storage = this.storage(),
                data;

            if (storage) {
                data = storage.get(path);
            }

            return data;
        },

        /**
         * Removes stored property.
         *
         * @param {String} property - Property to be removed from storage.
         * @returns {Element} Chainable.
         */
        removeStored: function (property) {
            var ns = this.storageConfig.namespace,
                path = utils.fullPath(ns, property);

            this.storage('remove', path);

            return this;
        },

        /**
         * Destroys current instance along with all of its' children.
         * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.
         */
        destroy: function (skipUpdate) {
            this._dropHandlers()
                ._clearRefs(skipUpdate);
        },

        /**
         * Removes events listeners.
         * @private
         *
         * @returns {Element} Chainable.
         */
        _dropHandlers: function () {
            this.off();

            if (_.isFunction(this.source)) {
                this.source().off(this.name);
            } else if (this.source) {
                this.source.off(this.name);
            }

            return this;
        },

        /**
         * Removes all references to current instance and
         * calls 'destroy' method on all of its' children.
         * @private
         * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.
         *
         * @returns {Element} Chainable.
         */
        _clearRefs: function (skipUpdate) {
            registry.remove(this.name);

            this.containers.forEach(function (parent) {
                parent.removeChild(this, skipUpdate);
            }, this);

            return this;
        },

        /**
         * Overrides 'EventsBus.trigger' method to implement events bubbling.
         *
         * @param {...*} arguments - Any number of arguments that should be passed to the events' handler.
         * @returns {Boolean} False if event bubbling was canceled.
         */
        bubble: function () {
            var args = _.toArray(arguments),
                bubble = this.trigger.apply(this, args),
                result;

            if (!bubble) {
                return false;
            }

            this.containers.forEach(function (parent) {
                result = parent.bubble.apply(parent, args);

                if (result === false) {
                    bubble = false;
                }
            });

            return !!bubble;
        },

        /**
         * Callback which fires when property under uniqueNs has changed.
         */
        onUniqueUpdate: function (name) {
            var active = name === this.name,
                property = this.uniqueProp;

            this[property](active);
        },

        /**
         * Clean data form data source.
         *
         * @returns {Element}
         */
        cleanData: function () {
            if (this.source && this.source.componentType === 'dataSource') {
                if (this.elems) {
                    _.each(this.elems(), function (val) {
                        val.cleanData();
                    });
                } else {
                    this.source.remove(this.dataScope);
                }
            }

            return this;
        },

        /**
         * Fallback data.
         */
        cacheData: function () {
            this.cachedComponent = utils.copy(this);
        },

        /**
         * Update configuration in component.
         *
         * @param {*} oldValue
         * @param {*} newValue
         * @param {String} path - path to value.
         * @returns {Element}
         */
        updateConfig: function (oldValue, newValue, path) {
            var names = path.split('.'),
                index = _.lastIndexOf(names, 'config') + 1;

            names = names.splice(index, names.length - index).join('.');
            this.set(names, newValue);

            return this;
        }
    }, Events, links);

    return Class.extend(Element);
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/lib/core/collection',[
    'underscore',
    'mageUtils',
    'uiRegistry',
    'uiElement'
], function (_, utils, registry, Element) {
    'use strict';

    /**
     * Removes non plain object items from the specified array.
     *
     * @param {Array} container - Array whose value should be filtered.
     * @returns {Array}
     */
    function compact(container) {
        return _.values(container).filter(utils.isObject);
    }

    /**
     * Defines index of an item in a specified container.
     *
     * @param {*} item - Item whose index should be defined.
     * @param {Array} container - Container upon which to perform search.
     * @returns {Number}
     */
    function _findIndex(item, container) {
        var index = _.findKey(container, function (value) {
            return value === item;
        });

        if (typeof index === 'undefined') {
            index = _.findKey(container, function (value) {
                return value && value.name === item;
            });
        }

        return typeof index === 'undefined' ? -1 : index;
    }

    /**
     * Inserts specified item into container at a specified position.
     *
     * @param {*} item - Item to be inserted into container.
     * @param {Array} container - Container of items.
     * @param {*} [position=-1] - Position at which item should be inserted.
     *      Position can represent:
     *          - specific index in container
     *          - item which might already be present in container
     *          - structure with one of these properties: after, before
     * @returns {Boolean|*}
     *      - true if element has changed its' position
     *      - false if nothing has changed
     *      - inserted value if it wasn't present in container
     */
    function _insertAt(item, container, position) {
        var currentIndex = _findIndex(item, container),
            newIndex,
            target;

        if (typeof position === 'undefined') {
            position = -1;
        } else if (typeof position === 'string') {
            position = isNaN(+position) ? position : +position;
        }

        newIndex = position;

        if (~currentIndex) {
            target = container.splice(currentIndex, 1)[0];

            if (typeof item === 'string') {
                item = target;
            }
        }

        if (typeof position !== 'number') {
            target = position.after || position.before || position;

            newIndex = _findIndex(target, container);

            if (~newIndex && (position.after || newIndex >= currentIndex)) {
                newIndex++;
            }
        }

        if (newIndex < 0) {
            newIndex += container.length + 1;
        }

        container[newIndex] ?
            container.splice(newIndex, 0, item) :
            container[newIndex] = item;

        return !~currentIndex ? item : currentIndex !== newIndex;
    }

    return Element.extend({
        defaults: {
            template: 'ui/collection',
            _elems: [],
            ignoreTmpls: {
                childDefaults: true
            }
        },

        /**
         * Initializes observable properties.
         *
         * @returns {Model} Chainable.
         */
        initObservable: function () {
            this._super()
                .observe({
                    elems: []
                });

            return this;
        },

        /**
         * Called when another element was added to current component.
         *
         * @param {Object} elem - Instance of an element that was added.
         * @returns {Collection} Chainable.
         */
        initElement: function (elem) {
            elem.initContainer(this);

            return this;
        },

        /**
         * Returns instance of a child found by provided index.
         *
         * @param {String} index - Index of a child.
         * @returns {Object}
         */
        getChild: function (index) {
            return _.findWhere(this.elems(), {
                index: index
            });
        },

        /**
         * Requests specified components to insert
         * them into 'elems' array starting from provided position.
         *
         * @param {(String|Array)} elems - Name of the component to insert.
         * @param {Number} [position=-1] - Position at which to insert elements.
         * @returns {Collection} Chainable.
         */
        insertChild: function (elems, position) {
            var container   = this._elems,
                insert      = this._insert.bind(this),
                update;

            if (!Array.isArray(elems)) {
                elems = [elems];
            }

            elems.map(function (item) {
                return item.elem ?
                    _insertAt(item.elem, container, item.position) :
                    _insertAt(item, container, position);
            }).forEach(function (item) {
                if (item === true) {
                    update = true;
                } else if (_.isString(item)) {
                    registry.get(item, insert);
                } else if (utils.isObject(item)) {
                    insert(item);
                }
            });

            if (update) {
                this._updateCollection();
            }

            return this;
        },

        /**
         * Removes specified child from collection.
         *
         * @param {(Object|String)} elem - Child or index of a child to be removed.
         * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.
         *
         * @returns {Collection} Chainable.
         */
        removeChild: function (elem, skipUpdate) {
            if (_.isString(elem)) {
                elem = this.getChild(elem);
            }

            if (elem) {
                utils.remove(this._elems, elem);

                if (!skipUpdate) {
                    this._updateCollection();
                }
            }

            return this;
        },

        /**
         * Destroys collection children with its' elements.
         */
        destroyChildren: function () {
            this.elems.each(function (elem) {
                elem.destroy(true);
            });

            this._updateCollection();
        },

        /**
         * Clear data. Call method "clear"
         * in child components
         *
         * @returns {Object} Chainable.
         */
        clear: function () {
            var elems = this.elems();

            _.each(elems, function (elem) {
                if (_.isFunction(elem.clear)) {
                    elem.clear();
                }
            }, this);

            return this;
        },

        /**
         * Checks if specified child exists in collection.
         *
         * @param {String} index - Index of a child.
         * @returns {Boolean}
         */
        hasChild: function (index) {
            return !!this.getChild(index);
        },

        /**
         * Creates 'async' wrapper for the specified child
         * using uiRegistry 'async' method and caches it
         * in a '_requested' components  object.
         *
         * @param {String} index - Index of a child.
         * @returns {Function} Async module wrapper.
         */
        requestChild: function (index) {
            var name = this.formChildName(index);

            return this.requestModule(name);
        },

        /**
         * Creates complete child name based on a provided index.
         *
         * @param {String} index - Index of a child.
         * @returns {String}
         */
        formChildName: function (index) {
            return this.name + '.' + index;
        },

        /**
         * Retrieves requested region.
         * Creates region if it was not created yet
         *
         * @returns {ObservableArray}
         */
        getRegion: function (name) {
            var regions = this.regions = this.regions || {};

            if (!regions[name]) {
                regions[name] = [];

                this.observe.call(regions, name);
            }

            return regions[name];
        },

        /**
         * Checks if the specified region has any elements
         * associated with it.
         *
         * @param {String} name
         * @returns {Boolean}
         */
        regionHasElements: function (name) {
            var region = this.getRegion(name);

            return region().length > 0;
        },

        /**
         * Replaces specified regions' data with a provided one.
         * Creates region if it was not created yet.
         *
         * @param {Array} items - New regions' data.
         * @param {String} name - Name of the region.
         * @returns {Collection} Chainable.
         */
        updateRegion: function (items, name) {
            this.getRegion(name)(items);

            return this;
        },

        /**
         * Destroys collection along with its' elements.
         */
        destroy: function () {
            this._super();

            this.elems.each('destroy');
        },

        /**
         * Inserts provided component into 'elems' array at a specified position.
         * @private
         *
         * @param {Object} elem - Element to insert.
         */
        _insert: function (elem) {
            var index = _.findKey(this._elems, function (value) {
                return value === elem.name;
            });

            if (typeof index !== 'undefined') {
                this._elems[index] = elem;
            }

            this._updateCollection()
                .initElement(elem);
        },

        /**
         * Synchronizes multiple elements arrays with a core '_elems' container.
         * Performs elemets grouping by theirs 'displayArea' property.
         * @private
         *
         * @returns {Collection} Chainable.
         */
        _updateCollection: function () {
            var _elems = compact(this._elems),
                grouped;

            grouped = _elems.filter(function (elem) {
                return elem.displayArea && _.isString(elem.displayArea);
            });
            grouped = _.groupBy(grouped, 'displayArea');

            _.each(grouped, this.updateRegion, this);

            _.each(this.regions, function (items) {
                var hasObsoleteComponents = items().length && !_.intersection(_elems, items()).length;

                if (hasObsoleteComponents) {
                    items.removeAll();
                }
            });

            this.elems(_elems);

            return this;
        },

        /**
         * Tries to call specified method of a current component,
         * otherwise delegates attempt to its' children.
         *
         * @param {String} target - Name of the method.
         * @param {...*} parameters - Arguments that will be passed to method.
         * @returns {*} Result of the method calls.
         */
        delegate: function (target) {
            var args = _.toArray(arguments);

            target = this[target];

            if (_.isFunction(target)) {
                return target.apply(this, args.slice(1));
            }

            return this._delegate(args);
        },

        /**
         * Calls 'delegate' method of all of it's children components.
         * @private
         *
         * @param {Array} args - An array of arguments to pass to the next delegation call.
         * @returns {Array} An array of delegation results.
         */
        _delegate: function (args) {
            var result;

            result = this.elems.map(function (elem) {
                var target;

                if (!_.isFunction(elem.delegate)) {
                    target = elem[args[0]];

                    if (_.isFunction(target)) {
                        return target.apply(elem, args.slice(1));
                    }
                } else {
                    return elem.delegate.apply(elem, args);
                }
            });

            return _.flatten(result);
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Checkout/js/view/cart-item-renderer',[
    'uiComponent'
], function (Component) {
    'use strict';

    return Component.extend({
        /**
         * Prepare the product name value to be rendered as HTML
         *
         * @param {String} productName
         * @return {String}
         */
        getProductNameUnsanitizedHtml: function (productName) {
            // product name has already escaped on backend
            return productName;
        },

        /**
         * Prepare the given option value to be rendered as HTML
         *
         * @param {String} optionValue
         * @return {String}
         */
        getOptionValueUnsanitizedHtml: function (optionValue) {
            // option value has already escaped on backend
            return optionValue;
        }
    });
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('Vaimo_BauhausSe/js/mock/auth-pop',[],function(){return{showModal:function(){return true},createPopUp:function(){return true}}})
;
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/template',[
    'underscore'
], function (_) {
    'use strict';

    /**
     * Checks if provided string is a valid DOM selector.
     *
     * @param {String} selector - Selector to be checked.
     * @returns {Boolean}
     */
    function isSelector(selector) {
        try {
            document.querySelector(selector);

            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * Unescapes characters used in underscore templates.
     *
     * @param {String} str - String to be processed.
     * @returns {String}
     */
    function unescape(str) {
        return str.replace(/&lt;%|%3C%/g, '<%').replace(/%&gt;|%%3E/g, '%>');
    }

    /**
     * If 'tmpl' is a valid selector, returns target node's innerHTML if found.
     * Else, returns empty string and emits console warning.
     * If 'tmpl' is not a selector, returns 'tmpl' as is.
     *
     * @param {String} tmpl
     * @returns {String}
     */
    function getTmplString(tmpl) {
        if (isSelector(tmpl)) {
            tmpl = document.querySelector(tmpl);

            if (tmpl) {
                tmpl = tmpl.innerHTML.trim();
            } else {
                console.warn('No template was found by selector: ' + tmpl);

                tmpl = '';
            }
        }

        return unescape(tmpl);
    }

    /**
     * Compiles or renders template provided either
     * by selector or by the template string.
     *
     * @param {String} tmpl - Template string or selector.
     * @param {(Object|Array|Function)} [data] - Data object with which to render template.
     * @returns {String|Function}
     */
    return function (tmpl, data) {
        var render;

        tmpl   = getTmplString(tmpl);
        render = _.template(tmpl);

        return !_.isUndefined(data) ?
            render(data) :
            render;
    };
});


define('text!ui/template/modal/modal-popup.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n\n<aside role="dialog"\n       class="modal-<%- data.type %> <%- data.modalClass %>\n               <% if(data.responsive){ %><%- data.responsiveClass %><% } %>\n               <% if(data.innerScroll){ %><%- data.innerScrollClass %><% } %>"\n       <% if(data.title){ %> aria-labelledby="modal-title-<%- data.id %>"<% } %>\n       aria-describedby="modal-content-<%- data.id %>"\n       data-role="modal"\n       data-type="<%- data.type %>"\n       tabindex="0">\n    <div data-role="focusable-start" tabindex="0"></div>\n    <div class="modal-inner-wrap"\n         data-role="focusable-scope">\n        <header class="modal-header">\n            <% if(data.title || data.subTitle){ %>\n            <h1 id="modal-title-<%- data.id %>" class="modal-title"\n                data-role="title">\n                <% if(data.title){ %>\n                    <%= data.title %>\n                <% } %>\n\n                <% if(data.subTitle){ %>\n                <span class="modal-subtitle"\n                      data-role="subTitle">\n                    <%= data.subTitle %>\n                </span>\n                <% } %>\n            </h1>\n            <% } %>\n            <button\n                class="action-close"\n                data-role="closeBtn"\n                type="button">\n                <span><%= data.closeText %></span>\n            </button>\n        </header>\n        <div id="modal-content-<%- data.id %>"\n            class="modal-content"\n            data-role="content"></div>\n        <% if(data.buttons.length > 0){ %>\n        <footer class="modal-footer">\n            <% _.each(data.buttons, function(button) { %>\n            <button\n                class="<%- button.class %>"\n                type="button"\n                data-role="action"><span><%= button.text %></span></button>\n            <% }); %>\n        </footer>\n        <% } %>\n    </div>\n    <div data-role="focusable-end" tabindex="0"></div>\n</aside>\n';});


define('text!ui/template/modal/modal-slide.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n\n<aside role="dialog"\n       class="modal-<%- data.type %> <%- data.modalClass %>\n               <% if(data.innerScroll){ %><%- data.innerScrollClass %><% } %>"\n       <% if(data.title){ %> aria-labelledby="modal-title-<%- data.id %>"<% } %>\n       aria-describedby="modal-content-<%- data.id %>"\n       data-role="modal"\n       data-type="<%- data.type %>"\n       tabindex="0">\n    <div data-role="focusable-start" tabindex="0"></div>\n    <div class="modal-inner-wrap"\n         data-role="focusable-scope">\n        <header class="modal-header">\n            <% if(data.title || data.subTitle){ %>\n            <h1 id="modal-title-<%- data.id %>" class="modal-title"\n                data-role="title">\n                <% if(data.title){ %>\n                    <%= data.title %>\n                <% } %>\n\n                <% if(data.subTitle){ %>\n                <span class="modal-subtitle"\n                      data-role="subTitle">\n                    <%= data.subTitle %>\n                </span>\n                <% } %>\n            </h1>\n            <% } %>\n            <button\n                class="action-close"\n                data-role="closeBtn"\n                type="button">\n                <span><%= data.closeText %></span>\n            </button>\n            <% if(data.buttons.length > 0){ %>\n            <div class="page-main-actions">\n                <div class="page-actions">\n                    <div class="page-actions-buttons">\n                        <% _.each(data.buttons, function(button) { %>\n                        <button\n                            class="<%- button.class %>"\n                            type="button"\n                            data-role="action"><span><%= button.text %></span>\n                        </button>\n                        <% }); %>\n                    </div>\n                </div>\n            </div>\n            <% } %>\n        </header>\n        <div id="modal-content-<%- data.id %>" class="modal-content" data-role="content"></div>\n    </div>\n    <div data-role="focusable-end" tabindex="0"></div>\n</aside>\n';});


define('text!ui/template/modal/modal-custom.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n\n<aside role="dialog"\n       class="modal-<%- data.type %> <%- data.modalClass %>\n       <% if(data.responsive){ %><%- data.responsiveClass %><% } %>\n       <% if(data.innerScroll){ %><%- data.innerScrollClass %><% } %>"\n       <% if(data.title){ %> aria-labelledby="modal-title-<%- data.id %>"<% } %>\n       aria-describedby="modal-content-<%- data.id %>"\n       data-role="modal"\n       data-type="<%- data.type %>"\n       tabindex="0">\n    <div data-role="focusable-start" tabindex="0"></div>\n    <div class="modal-inner-wrap"\n         data-role="focusable-scope">\n        <header class="modal-header">\n            <% if(data.title || data.subTitle){ %>\n            <h1 id="modal-title-<%- data.id %>" class="modal-title"\n                data-role="title">\n                <% if(data.title){ %>\n                    <%= data.title %>\n                <% } %>\n\n                <% if(data.subTitle){ %>\n                <span class="modal-subtitle"\n                      data-role="subTitle">\n                    <%= data.subTitle %>\n                </span>\n                <% } %>\n            </h1>\n            <% } %>\n            <button\n                class="action-close"\n                data-role="closeBtn"\n                type="button">\n                <span><%= data.closeText %></span>\n            </button>\n        </header>\n        <div id="modal-content-<%- data.id %>" class="modal-content" data-role="content"></div>\n        <% if(data.buttons.length > 0){ %>\n        <footer class="modal-footer">\n            <% _.each(data.buttons, function(button) { %>\n            <button class="<%- button.class %>"\n                    type="button"\n                    data-role="action">\n                <span><%= button.text %></span>\n            </button>\n            <% }); %>\n        </footer>\n        <% } %>\n    </div>\n    <div data-role="focusable-end" tabindex="0"></div>\n</aside>\n';});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/lib/key-codes',[], function () {
    'use strict';

    return {
        13: 'enterKey',
        27: 'escapeKey',
        40: 'pageDownKey',
        38: 'pageUpKey',
        32: 'spaceKey',
        9:  'tabKey',
        37: 'pageLeftKey',
        39: 'pageRightKey',
        17: 'ctrlKey',
        18: 'altKey',
        16: 'shiftKey',
        191: 'forwardSlashKey',
        66: 'bKey',
        73: 'iKey',
        85: 'uKey'
    };
});

/*!
 * zIndex plugin from jQuery UI Core - v1.10.4
 * http://jqueryui.com
 *
 * Copyright 2014 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/category/ui-core/
 */
define('jquery/z-index',[
    'jquery'
], function ($, undefined) {

// plugins
    $.fn.extend({
        zIndex: function (zIndex) {
            if (zIndex !== undefined) {
                return this.css("zIndex", zIndex);
            }

            if (this.length) {
                var elem = $(this[0]), position, value;
                while (elem.length && elem[0] !== document) {
                    // Ignore z-index if position is set to a value where z-index is ignored by the browser
                    // This makes behavior of this function consistent across browsers
                    // WebKit always returns auto if the element is positioned
                    position = elem.css("position");
                    if (position === "absolute" || position === "relative" || position === "fixed") {
                        // IE returns 0 when zIndex is not specified
                        // other browsers return a string
                        // we ignore the case of nested elements with an explicit value of 0
                        // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
                        value = parseInt(elem.css("zIndex"), 10);
                        if (!isNaN(value) && value !== 0) {
                            return value;
                        }
                    }
                    elem = elem.parent();
                }
            }

            return 0;
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/modal/modal',[
    'jquery',
    'underscore',
    'mage/template',
    'text!ui/template/modal/modal-popup.html',
    'text!ui/template/modal/modal-slide.html',
    'text!ui/template/modal/modal-custom.html',
    'Magento_Ui/js/lib/key-codes',
    'jquery-ui-modules/widget',
    'jquery-ui-modules/core',
    'mage/translate',
    'jquery/z-index'
], function ($, _, template, popupTpl, slideTpl, customTpl, keyCodes) {
    'use strict';

    /**
     * Detect browser transition end event.
     * @return {String|undefined} - transition event.
     */
    var transitionEvent = (function () {
        var transition,
            elementStyle = document.createElement('div').style,
            transitions = {
                'transition': 'transitionend',
                'OTransition': 'oTransitionEnd',
                'MozTransition': 'transitionend',
                'WebkitTransition': 'webkitTransitionEnd'
            };

        for (transition in transitions) {
            if (elementStyle[transition] !== undefined && transitions.hasOwnProperty(transition)) {
                return transitions[transition];
            }
        }
    })();

    /**
     * Modal Window Widget
     */
    $.widget('mage.modal', {
        options: {
            id: null,
            type: 'popup',
            title: '',
            subTitle: '',
            modalClass: '',
            focus: '[data-role="closeBtn"]',
            autoOpen: false,
            clickableOverlay: true,
            popupTpl: popupTpl,
            slideTpl: slideTpl,
            customTpl: customTpl,
            modalVisibleClass: '_show',
            parentModalClass: '_has-modal',
            innerScrollClass: '_inner-scroll',
            responsive: false,
            innerScroll: false,
            modalTitle: '[data-role="title"]',
            modalSubTitle: '[data-role="subTitle"]',
            modalBlock: '[data-role="modal"]',
            modalCloseBtn: '[data-role="closeBtn"]',
            modalContent: '[data-role="content"]',
            modalAction: '[data-role="action"]',
            focusableScope: '[data-role="focusable-scope"]',
            focusableStart: '[data-role="focusable-start"]',
            focusableEnd: '[data-role="focusable-end"]',
            appendTo: 'body',
            wrapperClass: 'modals-wrapper',
            overlayClass: 'modals-overlay',
            responsiveClass: 'modal-slide',
            trigger: '',
            modalLeftMargin: 45,
            closeText: $.mage.__('Close'),
            buttons: [{
                text: $.mage.__('Ok'),
                class: '',
                attr: {},

                /**
                 * Default action on button click
                 */
                click: function (event) {
                    this.closeModal(event);
                }
            }],
            keyEventHandlers: {

                /**
                 * Tab key press handler,
                 * set focus to elements
                 */
                tabKey: function () {
                    if (document.activeElement === this.modal[0]) {
                        this._setFocus('start');
                    }
                },

                /**
                 * Escape key press handler,
                 * close modal window
                 * @param {Object} event - event
                 */
                escapeKey: function (event) {
                    if (this.options.isOpen && this.modal.find(document.activeElement).length ||
                        this.options.isOpen && this.modal[0] === document.activeElement) {
                        this.closeModal(event);
                    }
                }
            }
        },

        /**
         * Creates modal widget.
         */
        _create: function () {
            _.bindAll(
                this,
                'keyEventSwitcher',
                '_tabSwitcher',
                'closeModal'
            );

            this.options.id = this.uuid;
            this.options.transitionEvent = transitionEvent;
            this._createWrapper();
            this._renderModal();
            this._createButtons();

            if (this.options.trigger) {
                $(document).on('click', this.options.trigger, _.bind(this.toggleModal, this));
            }
            this._on(this.modal.find(this.options.modalCloseBtn), {
                'click': this.options.modalCloseBtnHandler ? this.options.modalCloseBtnHandler : this.closeModal
            });
            this._on(this.element, {
                'openModal': this.openModal,
                'closeModal': this.closeModal
            });
            this.options.autoOpen ? this.openModal() : false;
        },

        /**
         * Returns element from modal node.
         * @return {Object} - element.
         */
        _getElem: function (elem) {
            return this.modal.find(elem);
        },

        /**
         * Gets visible modal count.
         * * @return {Number} - visible modal count.
         */
        _getVisibleCount: function () {
            var modals = this.modalWrapper.find(this.options.modalBlock);

            return modals.filter('.' + this.options.modalVisibleClass).length;
        },

        /**
         * Gets count of visible modal by slide type.
         * * @return {Number} - visible modal count.
         */
        _getVisibleSlideCount: function () {
            var elems = this.modalWrapper.find('[data-type="slide"]');

            return elems.filter('.' + this.options.modalVisibleClass).length;
        },

        /**
         * Listener key events.
         * Call handler function if it exists
         */
        keyEventSwitcher: function (event) {
            var key = keyCodes[event.keyCode];

            if (this.options.keyEventHandlers.hasOwnProperty(key)) {
                this.options.keyEventHandlers[key].apply(this, arguments);
            }
        },

        /**
         * Set title for modal.
         *
         * @param {String} title
         */
        setTitle: function (title) {
            var $title = this.modal.find(this.options.modalTitle),
                $subTitle = this.modal.find(this.options.modalSubTitle);

            $title.text(title);
            $title.append($subTitle);
        },

        /**
         * Set sub title for modal.
         *
         * @param {String} subTitle
         */
        setSubTitle: function (subTitle) {
            this.options.subTitle = subTitle;
            this.modal.find(this.options.modalSubTitle).html(subTitle);
        },

        /**
         * Toggle modal.
         * * @return {Element} - current element.
         */
        toggleModal: function () {
            if (this.options.isOpen === true) {
                this.closeModal();
            } else {
                this.openModal();
            }
        },

        /**
         * Open modal.
         * * @return {Element} - current element.
         */
        openModal: function () {
            this.options.isOpen = true;
            this.focussedElement = document.activeElement;
            this._createOverlay();
            this._setActive();
            this._setKeyListener();
            this.modal.one(this.options.transitionEvent, _.bind(this._setFocus, this, 'end', 'opened'));
            this.modal.one(this.options.transitionEvent, _.bind(this._trigger, this, 'opened'));
            this.modal.addClass(this.options.modalVisibleClass);

            if (!this.options.transitionEvent) {
                this._trigger('opened');
            }

            return this.element;
        },

        /**
         * Set focus to element.
         * @param {String} position - can be "start" and "end"
         *      positions.
         *      If position is "end" - sets focus to first
         *      focusable element in modal window scope.
         *      If position is "start" - sets focus to last
         *      focusable element in modal window scope
         *
         *  @param {String} type - can be "opened" or false
         *      If type is "opened" - looks to "this.options.focus"
         *      property and sets focus
         */
        _setFocus: function (position, type) {
            var focusableElements,
                infelicity;

            if (type === 'opened' && this.options.focus) {
                this.modal.find($(this.options.focus)).trigger('focus');
            } else if (type === 'opened' && !this.options.focus) {
                this.modal.find(this.options.focusableScope).trigger('focus');
            } else if (position === 'end') {
                this.modal.find(this.options.modalCloseBtn).trigger('focus');
            } else if (position === 'start') {
                infelicity = 2; //Constant for find last focusable element
                focusableElements = this.modal.find(':focusable');
                focusableElements.eq(focusableElements.length - infelicity).trigger('focus');
            }
        },

        /**
         * Set events listener when modal is opened.
         */
        _setKeyListener: function () {
            this.modal.find(this.options.focusableStart).on('focusin', this._tabSwitcher);
            this.modal.find(this.options.focusableEnd).on('focusin', this._tabSwitcher);
            this.modal.on('keydown', this.keyEventSwitcher);
        },

        /**
         * Remove events listener when modal is closed.
         */
        _removeKeyListener: function () {
            this.modal.find(this.options.focusableStart).off('focusin', this._tabSwitcher);
            this.modal.find(this.options.focusableEnd).off('focusin', this._tabSwitcher);
            this.modal.off('keydown', this.keyEventSwitcher);
        },

        /**
         * Switcher for focus event.
         * @param {Object} e - event
         */
        _tabSwitcher: function (e) {
            var target = $(e.target);

            if (target.is(this.options.focusableStart)) {
                this._setFocus('start');
            } else if (target.is(this.options.focusableEnd)) {
                this._setFocus('end');
            }
        },

        /**
         * Close modal.
         * * @return {Element} - current element.
         */
        closeModal: function () {
            var that = this;

            this._removeKeyListener();
            this.options.isOpen = false;
            this.modal.one(this.options.transitionEvent, function () {
                that._close();
            });
            this.modal.removeClass(this.options.modalVisibleClass);

            if (!this.options.transitionEvent) {
                that._close();
            }

            return this.element;
        },

        /**
         * Helper for closeModal function.
         */
        _close: function () {
            var trigger = _.bind(this._trigger, this, 'closed', this.modal);

            $(this.focussedElement).trigger('focus');
            this._destroyOverlay();
            this._unsetActive();
            _.defer(trigger, this);
        },

        /**
         * Set z-index and margin for modal and overlay.
         */
        _setActive: function () {
            var zIndex = this.modal.zIndex(),
                baseIndex = zIndex + this._getVisibleCount();

            if (this.modal.data('active')) {
                return;
            }

            this.modal.data('active', true);

            this.overlay.zIndex(++baseIndex);
            this.prevOverlayIndex = this.overlay.zIndex();
            this.modal.zIndex(this.overlay.zIndex() + 1);

            if (this._getVisibleSlideCount()) {
                this.modal.css('marginLeft', this.options.modalLeftMargin * this._getVisibleSlideCount());
            }
        },

        /**
         * Unset styles for modal and set z-index for previous modal.
         */
        _unsetActive: function () {
            this.modal.removeAttr('style');
            this.modal.data('active', false);

            if (this.overlay) {
                this.overlay.zIndex(this.prevOverlayIndex - 1);
            }
        },

        /**
         * Creates wrapper to hold all modals.
         */
        _createWrapper: function () {
            this.modalWrapper = $(this.options.appendTo).find('.' + this.options.wrapperClass);

            if (!this.modalWrapper.length) {
                this.modalWrapper = $('<div></div>')
                    .addClass(this.options.wrapperClass)
                    .appendTo(this.options.appendTo);
            }
        },

        /**
         * Compile template and append to wrapper.
         */
        _renderModal: function () {
            $(template(
                this.options[this.options.type + 'Tpl'],
                {
                    data: this.options
                })).appendTo(this.modalWrapper);
            this.modal = this.modalWrapper.find(this.options.modalBlock).last();
            this.element.appendTo(this._getElem(this.options.modalContent));

            if (this.element.is(':hidden')) {
                this.element.show();
            }
        },

        /**
         * Creates buttons pane.
         */
        _createButtons: function () {
            this.buttons = this._getElem(this.options.modalAction);
            _.each(this.options.buttons, function (btn, key) {
                var button = this.buttons[key];

                if (btn.attr) {
                    $(button).attr(btn.attr);
                }

                if (btn.class) {
                    $(button).addClass(btn.class);
                }

                if (!btn.click) {
                    btn.click = this.closeModal;
                }
                $(button).on('click', _.bind(btn.click, this));
            }, this);
        },

        /**
         * Creates overlay, append it to wrapper, set previous click event on overlay.
         */
        _createOverlay: function () {
            var events,
                outerClickHandler = this.options.outerClickHandler || this.closeModal;

            this.overlay = $('.' + this.options.overlayClass);

            if (!this.overlay.length) {
                $(this.options.appendTo).addClass(this.options.parentModalClass);
                this.overlay = $('<div></div>')
                    .addClass(this.options.overlayClass)
                    .appendTo(this.modalWrapper);
            }
            events = $._data(this.overlay.get(0), 'events');
            events ? this.prevOverlayHandler = events.click[0].handler : false;
            this.options.clickableOverlay ? this.overlay.off().on('click', outerClickHandler) : false;
        },

        /**
         * Destroy overlay.
         */
        _destroyOverlay: function () {
            if (this._getVisibleCount()) {
                this.overlay.off().on('click', this.prevOverlayHandler);
            } else {
                $(this.options.appendTo).removeClass(this.options.parentModalClass);
                this.overlay.remove();
                this.overlay = null;
            }
        }
    });

    return $.mage.modal;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/modal/confirm',[
    'jquery',
    'underscore',
    'mage/translate',
    'jquery-ui-modules/widget',
    'Magento_Ui/js/modal/modal'
], function ($, _, $t) {
    'use strict';

    $.widget('mage.confirm', $.mage.modal, {
        options: {
            modalClass: 'confirm',
            title: '',
            focus: '.action-accept',
            actions: {

                /**
                 * Callback always - called on all actions.
                 */
                always: function () {},

                /**
                 * Callback confirm.
                 */
                confirm: function () {},

                /**
                 * Callback cancel.
                 */
                cancel: function () {}
            },
            buttons: [{
                text: $t('Cancel'),
                class: 'action-secondary action-dismiss',

                /**
                 * Click handler.
                 */
                click: function (event) {
                    this.closeModal(event);
                }
            }, {
                text: $t('OK'),
                class: 'action-primary action-accept',

                /**
                 * Click handler.
                 */
                click: function (event) {
                    this.closeModal(event, true);
                }
            }]
        },

        /**
         * Create widget.
         */
        _create: function () {
            this._super();
            this.modal.find(this.options.modalCloseBtn).off().on('click', _.bind(this.closeModal, this));
            this.openModal();
        },

        /**
         * Remove modal window.
         */
        _remove: function () {
            this.modal.remove();
        },

        /**
         * Open modal window.
         */
        openModal: function () {
            return this._super();
        },

        /**
         * Close modal window.
         */
        closeModal: function (event, result) {
            result = result || false;

            if (result) {
                this.options.actions.confirm(event);
            } else {
                this.options.actions.cancel(event);
            }
            this.options.actions.always(event);
            this.element.on('confirmclosed', _.bind(this._remove, this));

            return this._super();
        }
    });

    return function (config) {
        return $('<div></div>').html(config.content).confirm(config);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/modal/alert',[
    'jquery',
    'underscore',
    'jquery-ui-modules/widget',
    'Magento_Ui/js/modal/confirm',
    'mage/translate'
], function ($, _) {
    'use strict';

    $.widget('mage.alert', $.mage.confirm, {
        options: {
            modalClass: 'confirm',
            title: $.mage.__('Attention'),
            actions: {

                /**
                 * Callback always - called on all actions.
                 */
                always: function () {}
            },
            buttons: [{
                text: $.mage.__('OK'),
                class: 'action-primary action-accept',

                /**
                 * Click handler.
                 */
                click: function () {
                    this.closeModal(true);
                }
            }]
        },

        /**
         * Close modal window.
         */
        closeModal: function () {
            this.options.actions.always();
            this.element.on('alertclosed', _.bind(this._remove, this));

            return this._super();
        }
    });

    return function (config) {
        return $('<div></div>').html(config.content).alert(config);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/cookies',[
    'jquery',
    'mage/mage',
    'js-cookie/cookie-wrapper'
], function ($) {
    'use strict';

    /**
     * Helper for cookies manipulation
     * @returns {CookieHelper}
     * @constructor
     */
    var CookieHelper = function () {

        /**
         * Cookie default values.
         * @type {Object}
         */
        this.defaults = {
            expires: null,
            path: '/',
            domain: null,
            secure: false,
            lifetime: null,
            samesite: 'lax'
        };

        /**
         * Calculate cookie expiration date based on its lifetime.
         * @param {Object} options - Cookie option values
         * @return {Date|null} Calculated cookie expiration date or null if no lifetime provided.
         * @private
         */
        function lifetimeToExpires(options, defaults) {
            var expires,
                lifetime;

            lifetime = options.lifetime || defaults.lifetime;

            if (lifetime && lifetime > 0) {
                expires = options.expires || new Date();

                return new Date(expires.getTime() + lifetime * 1000);
            }

            return null;
        }

        /**
         * Set a cookie's value by cookie name based on optional cookie options.
         * @param {String} name - The name of the cookie.
         * @param {String} value - The cookie's value.
         * @param {Object} options - Optional options (e.g. lifetime, expires, path, etc.)
         */
        this.set = function (name, value, options) {
            var expires,
                path,
                domain,
                secure,
                samesite;

            options = $.extend({}, this.defaults, options || {});
            expires = lifetimeToExpires(options, this.defaults) || options.expires;
            path = options.path;
            domain = options.domain;
            secure = options.secure;
            samesite = options.samesite;

            document.cookie = name + '=' + encodeURIComponent(value) +
                (expires ? '; expires=' + expires.toUTCString() :  '') +
                (path ? '; path=' + path : '') +
                (domain ? '; domain=' + domain : '') +
                (secure ? '; secure' : '') +
                '; samesite=' + (samesite ? samesite : 'lax');
        };

        /**
         * Get a cookie's value by cookie name.
         * @param {String} name  - The name of the cookie.
         * @return {(null|String)}
         */
        this.get = function (name) {
            var arg = name + '=',
                aLength = arg.length,
                cookie = document.cookie,
                cLength = cookie.length,
                i = 0,
                j = 0;

            while (i < cLength) {
                j = i + aLength;

                if (cookie.substring(i, j) === arg) {
                    return this.getCookieVal(j);
                }
                i = cookie.indexOf(' ', i) + 1;

                if (i === 0) {
                    break;
                }
            }

            return null;
        };

        /**
         * Clear a cookie's value by name.
         * @param {String} name - The name of the cookie being cleared.
         */
        this.clear = function (name) {
            if (this.get(name)) {
                this.set(name, '', {
                    expires: new Date('Jan 01 1970 00:00:01 GMT')
                });
            }
        };

        /**
         * Return URI decoded cookie component value (e.g. expires, path, etc.) based on a
         * numeric offset in the document's cookie value.
         * @param {Number} offset - Offset into the document's cookie value.
         * @return {String}
         */
        this.getCookieVal = function (offset) {
            var cookie = document.cookie,
                endstr = cookie.indexOf(';', offset);

            if (endstr === -1) {
                endstr = cookie.length;
            }

            return decodeURIComponent(cookie.substring(offset, endstr));
        };

        return this;
    };

    $.extend(true, $, {
        mage: {
            cookies: new CookieHelper()
        }
    });

    return function (pageOptions) {
        $.extend($.mage.cookies.defaults, pageOptions);
        $.extend($.cookie.defaults, $.mage.cookies.defaults);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Checkout/js/sidebar',[
    'jquery',
    'Magento_Customer/js/model/authentication-popup',
    'Magento_Customer/js/customer-data',
    'Magento_Ui/js/modal/alert',
    'Magento_Ui/js/modal/confirm',
    'underscore',
    'jquery-ui-modules/widget',
    'mage/decorate',
    'mage/collapsible',
    'mage/cookies',
    'jquery-ui-modules/effect-fade'
], function ($, authenticationPopup, customerData, alert, confirm, _) {
    'use strict';

    $.widget('mage.sidebar', {
        options: {
            isRecursive: true,
            minicart: {
                maxItemsVisible: 3
            }
        },
        scrollHeight: 0,
        shoppingCartUrl: window.checkout.shoppingCartUrl,

        /**
         * Create sidebar.
         * @private
         */
        _create: function () {
            this._initContent();
        },

        /**
         * Update sidebar block.
         */
        update: function () {
            $(this.options.targetElement).trigger('contentUpdated');
            this._calcHeight();
        },

        /**
         * @private
         */
        _initContent: function () {
            var self = this,
                events = {};

            this.element.decorate('list', this.options.isRecursive);

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.button.close] = function (event) {
                event.stopPropagation();
                $(self.options.targetElement).dropdownDialog('close');
            };
            events['click ' + this.options.button.checkout] = $.proxy(function () {
                var cart = customerData.get('cart'),
                    customer = customerData.get('customer'),
                    element = $(this.options.button.checkout);

                if (!customer().firstname && cart().isGuestCheckoutAllowed === false) {
                    // set URL for redirect on successful login/registration. It's postprocessed on backend.
                    $.cookie('login_redirect', this.options.url.checkout);

                    if (this.options.url.isRedirectRequired) {
                        element.prop('disabled', true);
                        location.href = this.options.url.loginUrl;
                    } else {
                        authenticationPopup.showModal();
                    }

                    return false;
                }
                element.prop('disabled', true);
                location.href = this.options.url.checkout;
            }, this);

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.button.remove] =  function (event) {
                event.stopPropagation();
                confirm({
                    content: self.options.confirmMessage,
                    actions: {
                        /** @inheritdoc */
                        confirm: function () {
                            self._removeItem($(event.currentTarget));
                        },

                        /** @inheritdoc */
                        always: function (e) {
                            e.stopImmediatePropagation();
                        }
                    }
                });
            };

            /**
             * @param {jQuery.Event} event
             */
            events['keyup ' + this.options.item.qty] = function (event) {
                self._showItemButton($(event.target));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['change ' + this.options.item.qty] = function (event) {
                self._showItemButton($(event.target));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['change ' + this.options.item.qty] = function (event) {
                self._showItemButton($(event.target));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.item.button] = function (event) {
                event.stopPropagation();
                self._updateItemQty($(event.currentTarget));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['focusout ' + this.options.item.qty] = function (event) {
                self._validateQty($(event.currentTarget));
            };

            this._on(this.element, events);
            this._calcHeight();
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _showItemButton: function (elem) {
            var itemId = elem.data('cart-item'),
                itemQty = elem.data('item-qty');

            if (this._isValidQty(itemQty, elem.val())) {
                $('#update-cart-item-' + itemId).show('fade', 300);
            } else if (elem.val() == 0) { //eslint-disable-line eqeqeq
                this._hideItemButton(elem);
            } else {
                this._hideItemButton(elem);
            }
        },

        /**
         * @param {*} origin - origin qty. 'data-item-qty' attribute.
         * @param {*} changed - new qty.
         * @returns {Boolean}
         * @private
         */
        _isValidQty: function (origin, changed) {
            return origin != changed && //eslint-disable-line eqeqeq
                changed.length > 0 &&
                changed - 0 == changed && //eslint-disable-line eqeqeq
                changed - 0 > 0;
        },

        /**
         * @param {Object} elem
         * @private
         */
        _validateQty: function (elem) {
            var itemQty = elem.data('item-qty');

            if (!this._isValidQty(itemQty, elem.val())) {
                elem.val(itemQty);
            }
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _hideItemButton: function (elem) {
            var itemId = elem.data('cart-item');

            $('#update-cart-item-' + itemId).hide('fade', 300);
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _updateItemQty: function (elem) {
            var itemId = elem.data('cart-item');

            this._ajax(this.options.url.update, {
                'item_id': itemId,
                'item_qty': $('#cart-item-' + itemId + '-qty').val()
            }, elem, this._updateItemQtyAfter);
        },

        /**
         * Update content after update qty
         *
         * @param {HTMLElement} elem
         */
        _updateItemQtyAfter: function (elem) {
            var productData = this._getProductById(Number(elem.data('cart-item')));

            if (!_.isUndefined(productData)) {
                $(document).trigger('ajax:updateCartItemQty');

                if (window.location.href === this.shoppingCartUrl) {
                    window.location.reload(false);
                }
            }
            this._hideItemButton(elem);
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _removeItem: function (elem) {
            var itemId = elem.data('cart-item');

            this._ajax(this.options.url.remove, {
                'item_id': itemId
            }, elem, this._removeItemAfter);
        },

        /**
         * Update content after item remove
         *
         * @param {Object} elem
         * @private
         */
        _removeItemAfter: function (elem) {
            var productData = this._getProductById(Number(elem.data('cart-item')));

            if (!_.isUndefined(productData)) {
                $(document).trigger('ajax:removeFromCart', {
                    productIds: [productData['product_id']],
                    productInfo: [
                        {
                            'id': productData['product_id']
                        }
                    ]
                });

                if (window.location.href.indexOf(this.shoppingCartUrl) === 0) {
                    window.location.reload();
                }
            }
        },

        /**
         * Retrieves product data by Id.
         *
         * @param {Number} productId - product Id
         * @returns {Object|undefined}
         * @private
         */
        _getProductById: function (productId) {
            return _.find(customerData.get('cart')().items, function (item) {
                return productId === Number(item['item_id']);
            });
        },

        /**
         * @param {String} url - ajax url
         * @param {Object} data - post data for ajax call
         * @param {Object} elem - element that initiated the event
         * @param {Function} callback - callback method to execute after AJAX success
         */
        _ajax: function (url, data, elem, callback) {
            $.extend(data, {
                'form_key': $.mage.cookies.get('form_key')
            });

            $.ajax({
                url: url,
                data: data,
                type: 'post',
                dataType: 'json',
                context: this,

                /** @inheritdoc */
                beforeSend: function () {
                    elem.attr('disabled', 'disabled');
                },

                /** @inheritdoc */
                complete: function () {
                    elem.attr('disabled', null);
                }
            })
                .done(function (response) {
                    var msg;

                    if (response.success) {
                        callback.call(this, elem, response);
                    } else {
                        msg = response['error_message'];

                        if (msg) {
                            alert({
                                content: msg
                            });
                        }
                    }
                })
                .fail(function (error) {
                    console.log(JSON.stringify(error));
                });
        },

        /**
         * Calculate height of minicart list
         *
         * @private
         */
        _calcHeight: function () {
            var self = this,
                height = 0,
                counter = this.options.minicart.maxItemsVisible,
                target = $(this.options.minicart.list),
                outerHeight;

            self.scrollHeight = 0;
            target.children().each(function () {

                if ($(this).find('.options').length > 0) {
                    $(this).collapsible();
                }
                outerHeight = $(this).outerHeight(true);

                if (counter-- > 0) {
                    height += outerHeight;
                }
                self.scrollHeight += outerHeight;
            });

            target.parent().height(height);
        }
    });

    return $.mage.sidebar;
});

/**
 *
 * Author: Gianluca Guarini
 * Contact: gianluca.guarini@gmail.com
 * Website: http://www.gianlucaguarini.com/
 * Twitter: @gianlucaguarini
 *
 * Copyright (c) Gianluca Guarini
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 **/
/* global jQuery */
(function(doc, win) {
  if (typeof doc.createEvent !== 'function') return false // no tap events here
  // helpers
  var pointerEvent = function(type) {
      var lo = type.toLowerCase(),
        ms = 'MS' + type
      return navigator.msPointerEnabled ? ms : window.PointerEvent ? lo : false
    },
    touchEvent = function(name) {
      return 'on' + name in window ? name : false
    },
    defaults = {
      useJquery: !win.IGNORE_JQUERY && typeof jQuery !== 'undefined',
      swipeThreshold: win.SWIPE_THRESHOLD || 100,
      tapThreshold: win.TAP_THRESHOLD || 150, // range of time where a tap event could be detected
      dbltapThreshold: win.DBL_TAP_THRESHOLD || 200, // delay needed to detect a double tap
      longtapThreshold: win.LONG_TAP_THRESHOLD || 1000, // delay needed to detect a long tap
      tapPrecision: win.TAP_PRECISION / 2 || 60 / 2, // touch events boundaries ( 60px by default )
      justTouchEvents: win.JUST_ON_TOUCH_DEVICES
    },
    // was initially triggered a "touchstart" event?
    wasTouch = false,
    touchevents = {
      touchstart: touchEvent('touchstart') || pointerEvent('PointerDown'),
      touchend: touchEvent('touchend') || pointerEvent('PointerUp'),
      touchmove: touchEvent('touchmove') || pointerEvent('PointerMove')
    },
    isTheSameFingerId = function(e) {
      return !e.pointerId || typeof pointerId === 'undefined' || e.pointerId === pointerId
    },
    setListener = function(elm, events, callback) {
      var eventsArray = events.split(' '),
        i = eventsArray.length

      while (i--) {
        elm.addEventListener(eventsArray[i], callback, false)
      }
    },
    getPointerEvent = function(event) {
      var hasTargetTouches = Boolean(event.targetTouches && event.targetTouches.length)
      switch (true) {
      case Boolean(event.target.touches):
        return event.target.touches[0]
      case hasTargetTouches && typeof event.targetTouches[0].pageX !== 'undefined':
        return event.targetTouches[0]
      case hasTargetTouches && Boolean(event.targetTouches[0].touches):
        return event.targetTouches[0].touches[0]
      default:
        return event
      }
    },
    isMultipleTouches = function(event) {
      return (event.targetTouches || event.target.touches || []).length > 1
    },
    getTimestamp = function() {
      return new Date().getTime()
    },
    sendEvent = function(elm, eventName, originalEvent, data) {
      var customEvent = doc.createEvent('Event')
      customEvent.originalEvent = originalEvent
      data = data || {}
      data.x = currX
      data.y = currY

      // jquery
      if (defaults.useJquery) {
        customEvent = jQuery.Event(eventName, {originalEvent: originalEvent})
        jQuery(elm).trigger(customEvent, data)
      }

      // addEventListener
      if (customEvent.initEvent) {
        for (var key in data) {
          customEvent[key] = data[key]
        }

        customEvent.initEvent(eventName, true, true)
        elm.dispatchEvent(customEvent)
      }

      // detect all the inline events
      // also on the parent nodes
      while (elm) {
        // inline
        if (elm['on' + eventName])
          elm['on' + eventName](customEvent)
        elm = elm.parentNode
      }

    },

    onTouchStart = function(e) {
      /**
       * Skip all the mouse events
       * events order:
       * Chrome:
       *   touchstart
       *   touchmove
       *   touchend
       *   mousedown
       *   mousemove
       *   mouseup <- this must come always after a "touchstart"
       *
       * Safari
       *   touchstart
       *   mousedown
       *   touchmove
       *   mousemove
       *   touchend
       *   mouseup <- this must come always after a "touchstart"
       */

      if (!isTheSameFingerId(e) || isMultipleTouches(e)) return

      pointerId = e.pointerId

      // it looks like it was a touch event!
      if (e.type !== 'mousedown')
        wasTouch = true

      // skip this event we don't need to track it now
      if (e.type === 'mousedown' && wasTouch) return

      var pointer = getPointerEvent(e)

      // caching the current x
      cachedX = currX = pointer.pageX
      // caching the current y
      cachedY = currY = pointer.pageY

      longtapTimer = setTimeout(function() {
        sendEvent(e.target, 'longtap', e)
        target = e.target
      }, defaults.longtapThreshold)

      // we will use these variables on the touchend events
      timestamp = getTimestamp()

      tapNum++

    },
    onTouchEnd = function(e) {
      if (!isTheSameFingerId(e) || isMultipleTouches(e)) return

      pointerId = undefined

      // skip the mouse events if previously a touch event was dispatched
      // and reset the touch flag
      if (e.type === 'mouseup' && wasTouch) {
        wasTouch = false
        return
      }

      var eventsArr = [],
        now = getTimestamp(),
        deltaY = cachedY - currY,
        deltaX = cachedX - currX

      // clear the previous timer if it was set
      clearTimeout(dblTapTimer)
      // kill the long tap timer
      clearTimeout(longtapTimer)

      if (deltaX <= -defaults.swipeThreshold)
        eventsArr.push('swiperight')

      if (deltaX >= defaults.swipeThreshold)
        eventsArr.push('swipeleft')

      if (deltaY <= -defaults.swipeThreshold)
        eventsArr.push('swipedown')

      if (deltaY >= defaults.swipeThreshold)
        eventsArr.push('swipeup')

      if (eventsArr.length) {
        for (var i = 0; i < eventsArr.length; i++) {
          var eventName = eventsArr[i]
          sendEvent(e.target, eventName, e, {
            distance: {
              x: Math.abs(deltaX),
              y: Math.abs(deltaY)
            }
          })
        }
        // reset the tap counter
        tapNum = 0
      } else {

        if (
          cachedX >= currX - defaults.tapPrecision &&
          cachedX <= currX + defaults.tapPrecision &&
          cachedY >= currY - defaults.tapPrecision &&
          cachedY <= currY + defaults.tapPrecision
        ) {
          if (timestamp + defaults.tapThreshold - now >= 0)
          {
            // Here you get the Tap event
            sendEvent(e.target, tapNum >= 2 && target === e.target ? 'dbltap' : 'tap', e)
            target= e.target
          }
        }

        // reset the tap counter
        dblTapTimer = setTimeout(function() {
          tapNum = 0
        }, defaults.dbltapThreshold)

      }
    },
    onTouchMove = function(e) {
      if (!isTheSameFingerId(e)) return
      // skip the mouse move events if the touch events were previously detected
      if (e.type === 'mousemove' && wasTouch) return

      var pointer = getPointerEvent(e)
      currX = pointer.pageX
      currY = pointer.pageY
    },
    tapNum = 0,
    pointerId, currX, currY, cachedX, cachedY, timestamp, target, dblTapTimer, longtapTimer

  //setting the events listeners
  // we need to debounce the callbacks because some devices multiple events are triggered at same time
  setListener(doc, touchevents.touchstart + (defaults.justTouchEvents ? '' : ' mousedown'), onTouchStart)
  setListener(doc, touchevents.touchend + (defaults.justTouchEvents ? '' : ' mouseup'), onTouchEnd)
  setListener(doc, touchevents.touchmove + (defaults.justTouchEvents ? '' : ' mousemove'), onTouchMove)

  // Configure the tocca default options at any time
  win.tocca = function(options) {
    for (var opt in options) {
      defaults[opt] = options[opt]
    }

    return defaults
  }
})(document, window)
;
define("jquery/jquery.mobile.custom", function(){});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/dataPost',[
    'jquery',
    'mage/template',
    'Magento_Ui/js/modal/confirm',
    'jquery-ui-modules/widget'
], function ($, mageTemplate, uiConfirm) {
    'use strict';

    $.widget('mage.dataPost', {
        options: {
            formTemplate: '<form action="<%- data.action %>" method="post">' +
            '<% _.each(data.data, function(value, index) { %>' +
            '<input name="<%- index %>" value="<%- value %>">' +
            '<% }) %></form>',
            postTrigger: ['a[data-post]', 'button[data-post]', 'span[data-post]'],
            formKeyInputSelector: 'input[name="form_key"]'
        },

        /** @inheritdoc */
        _create: function () {
            this._bind();
        },

        /** @inheritdoc */
        _bind: function () {
            var events = {};

            $.each(this.options.postTrigger, function (index, value) {
                events['click ' + value] = '_postDataAction';
            });

            this._on(events);
        },

        /**
         * Handler for click.
         *
         * @param {Object} e
         * @private
         */
        _postDataAction: function (e) {
            var params = $(e.currentTarget).data('post');

            e.preventDefault();
            this.postData(params);
        },

        /**
         * Data post action.
         *
         * @param {Object} params
         */
        postData: function (params) {
            var formKey = $(this.options.formKeyInputSelector).val(),
                $form, input;

            if (formKey) {
                params.data['form_key'] = formKey;
            }

            $form = $(mageTemplate(this.options.formTemplate, {
                data: params
            }));

            if (params.files) {
                $form[0].enctype = 'multipart/form-data';
                $.each(params.files, function (key, files) {
                    if (files instanceof FileList) {
                        input = document.createElement('input');
                        input.type = 'file';
                        input.name = key;
                        input.files = files;
                        $form[0].appendChild(input);
                    }
                });
            }

            if (params.data.confirmation) {
                uiConfirm({
                    content: params.data.confirmationMessage,
                    actions: {
                        /** @inheritdoc */
                        confirm: function () {
                            $form.appendTo('body').hide().trigger('submit');
                        }
                    }
                });
            } else {
                $form.appendTo('body').hide().trigger('submit');
            }
        }
    });

    $(document).dataPost();

    return $.mage.dataPost;
});


define('text!ui/template/tooltip/tooltip.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<div data-tooltip="tooltip-wrapper" class="data-tooltip-wrapper <%= data.tooltipClasses %>">\n    <div class="data-tooltip-tail"></div>\n    <div class="data-tooltip">\n        <% if(data.closeButton){ %>\n            <button type="button" class="action-close">\n                <span translate="\'Close\'"></span>\n            </button>\n        <% } %>\n        <div class="data-tooltip-content"></div>\n    </div>\n</div>\n';});


define('text!ui/template/collection.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<each args="data: elems, as: \'element\'">\n    <render if="hasTemplate()"></render>\n</each>\n';});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/**
 * Is being used by knockout template engine to store template to.
 */
define('Magento_Ui/js/lib/knockout/template/observable_source',[
    'ko',
    'uiClass'
], function (ko, Class) {
    'use strict';

    return Class.extend({

        /**
         * Initializes templateName, _data, nodes properties.
         *
         * @param  {template} template - identifier of template
         */
        initialize: function (template) {
            this.templateName = template;
            this._data = {};
            this.nodes = ko.observable([]);
        },

        /**
         * Data setter. If only one arguments passed, returns corresponding value.
         * Else, writes into it.
         * @param  {String} key - key to write to or to read from
         * @param  {*} value
         * @return {*} - if 1 arg provided, Returns _data[key] property
         */
        data: function (key, value) {
            if (arguments.length === 1) {
                return this._data[key];
            }

            this._data[key] = value;
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/template/loader',[
    'jquery'
], function ($) {
    'use strict';

    var licenseRegExp   = /<!--[\s\S]*?-->/,
        defaultPlugin   = 'text',
        defaultExt      = 'html';

    /**
     * Checks of provided string contains a file extension.
     *
     * @param {String} str - String to be checked.
     * @returns {Boolean}
     */
    function hasFileExtension(str) {
        return !!~str.indexOf('.') && !!str.split('.').pop();
    }

    /**
     * Checks if provided string contains a requirejs's plugin reference.
     *
     * @param {String} str - String to be checked.
     * @returns {Boolean}
     */
    function hasPlugin(str) {
        return !!~str.indexOf('!');
    }

    /**
     * Checks if provided string is a full path to the file.
     *
     * @param {String} str - String to be checked.
     * @returns {Boolean}
     */
    function isFullPath(str) {
        return !!~str.indexOf('://');
    }

    /**
     * Removes license comment from the provided string.
     *
     * @param {String} content - String to be processed.
     * @returns {String}
     */
    function removeLicense(content) {
        return content.replace(licenseRegExp, function (match) {
            return ~match.indexOf('/**') ? '' : match;
        });
    }

    return {

        /**
         * Attempts to extract template by provided path from
         * a DOM element and falls back to a file loading if
         * none of the DOM nodes was found.
         *
         * @param {String} path - Path to the template or a DOM selector.
         * @returns {jQueryPromise}
         */
        loadTemplate: function (path) {
            var content = this.loadFromNode(path),
                defer;

            if (content) {
                defer = $.Deferred();

                defer.resolve(content);

                return defer.promise();
            }

            return this.loadFromFile(path);
        },

        /**
         * Loads template from external file by provided
         * path, which will be preliminary formatted.
         *
         * @param {String} path - Path to the template.
         * @returns {jQueryPromise}
         */
        loadFromFile: function (path) {
            var loading = $.Deferred();

            path = this.formatPath(path);

            require([path], function (template) {
                template = removeLicense(template);
                loading.resolve(template);
            }, function (err) {
                loading.reject(err);
            });

            return loading.promise();
        },

        /**
         * Attempts to extract content of a node found by provided selector.
         *
         * @param {String} selector - Node's selector (not necessary valid).
         * @returns {String|Boolean} If specified node doesn't exists
         *      'false' will be returned, otherwise returns node's content.
         */
        loadFromNode: function (selector) {
            var node;

            try {
                node =
                    document.getElementById(selector) ||
                    document.querySelector(selector);

                return node ? node.innerHTML : false;
            } catch (e) {
                return false;
            }
        },

        /**
         * Adds requirejs's plugin and file extension to
         * to the provided string if it's necessary.
         *
         * @param {String} path - Path to be processed.
         * @returns {String} Formatted path.
         */
        formatPath: function (path) {
            var result = path;

            if (!hasPlugin(path)) {
                result = defaultPlugin + '!' + result;
            }

            if (isFullPath(path)) {
                return result;
            }

            if (!hasFileExtension(path)) {
                result += '.' + defaultExt;
            }

            return result.replace(/^([^\/]+)/g, '$1/template');
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/template/renderer',[
    'jquery',
    'underscore',
    './loader'
], function ($, _, loader) {
    'use strict';

    var colonReg       = /\\:/g,
        renderedTemplatePromises = {},
        attributes     = {},
        elements       = {},
        globals        = [],
        renderer,
        preset;

    renderer = {

        /**
         * Loads template by provided path and
         * than converts it's content to html.
         *
         * @param {String} tmplPath - Path to the template.
         * @returns {jQueryPromise}
         * @alias getRendered
         */
        render: function (tmplPath) {
            var cachedPromise = renderedTemplatePromises[tmplPath];

            if (!cachedPromise) {
                cachedPromise = renderedTemplatePromises[tmplPath] = loader
                    .loadTemplate(tmplPath)
                    .then(renderer.parseTemplate);
            }

            return cachedPromise;
        },

        /**
         * @ignore
         */
        getRendered: function (tmplPath) {
            return renderer.render(tmplPath);
        },

        /**
         * Parses provided string as html content
         * and returns an array of DOM elements.
         *
         * @param {String} html - String to be processed.
         * @returns {Array}
         */
        parseTemplate: function (html) {
            var fragment = document.createDocumentFragment();

            $(fragment).append(html);

            return renderer.normalize(fragment);
        },

        /**
         * Processes custom attributes and nodes of provided DOM element.
         *
         * @param {HTMLElement} content - Element to be processed.
         * @returns {Array} An array of content's child nodes.
         */
        normalize: function (content) {
            globals.forEach(function (handler) {
                handler(content);
            });

            return _.toArray(content.childNodes);
        },

        /**
         * Adds new global content handler.
         *
         * @param {Function} handler - Function which will be invoked for
         *      an every content passed to 'normalize' method.
         * @returns {Renderer} Chainable.
         */
        addGlobal: function (handler) {
            if (!_.contains(globals, handler)) {
                globals.push(handler);
            }

            return this;
        },

        /**
         * Removes specified global content handler.
         *
         * @param {Function} handler - Handler to be removed.
         * @returns {Renderer} Chainable.
         */
        removeGlobal: function (handler) {
            var index = globals.indexOf(handler);

            if (~index) {
                globals.splice(index, 1);
            }

            return this;
        },

        /**
         * Adds new custom attribute handler.
         *
         * @param {String} id - Attribute identifier.
         * @param {(Object|Function)} [config={}]
         * @returns {Renderer} Chainable.
         */
        addAttribute: function (id, config) {
            var data = {
                name: id,
                binding: id,
                handler: renderer.handlers.attribute
            };

            if (_.isFunction(config)) {
                data.handler = config;
            } else if (_.isObject(config)) {
                _.extend(data, config);
            }

            data.id = id;
            attributes[id] = data;

            return this;
        },

        /**
         * Removes specified attribute handler.
         *
         * @param {String} id - Attribute identifier.
         * @returns {Renderer} Chainable.
         */
        removeAttribute: function (id) {
            delete attributes[id];

            return this;
        },

        /**
         * Adds new custom node handler.
         *
         * @param {String} id - Node identifier.
         * @param {(Object|Function)} [config={}]
         * @returns {Renderer} Chainable.
         */
        addNode: function (id, config) {
            var data = {
                name: id,
                binding: id,
                handler: renderer.handlers.node
            };

            if (_.isFunction(config)) {
                data.handler = config;
            } else if (_.isObject(config)) {
                _.extend(data, config);
            }

            data.id = id;
            elements[id] = data;

            return this;
        },

        /**
         * Removes specified custom node handler.
         *
         * @param {String} id - Node identifier.
         * @returns {Renderer} Chainable.
         */
        removeNode: function (id) {
            delete elements[id];

            return this;
        },

        /**
         * Checks if provided DOM element is a custom node.
         *
         * @param {HTMLElement} node - Node to be checked.
         * @returns {Boolean}
         */
        isCustomNode: function (node) {
            return _.some(elements, function (elem) {
                return elem.name.toUpperCase() === node.tagName;
            });
        },

        /**
         * Processes custom attributes of a content's child nodes.
         *
         * @param {HTMLElement} content - DOM element to be processed.
         */
        processAttributes: function (content) {
            var repeat;

            repeat = _.some(attributes, function (attr) {
                var attrName = attr.name,
                    nodes    = content.querySelectorAll('[' + attrName + ']'),
                    handler  = attr.handler;

                return _.toArray(nodes).some(function (node) {
                    var data = node.getAttribute(attrName);

                    return handler(node, data, attr) === true;
                });
            });

            if (repeat) {
                renderer.processAttributes(content);
            }
        },

        /**
         * Processes custom nodes of a provided content.
         *
         * @param {HTMLElement} content - DOM element to be processed.
         */
        processNodes: function (content) {
            var repeat;

            repeat = _.some(elements, function (element) {
                var nodes   = content.querySelectorAll(element.name),
                    handler = element.handler;

                return _.toArray(nodes).some(function (node) {
                    var data = node.getAttribute('args');

                    return handler(node, data, element) === true;
                });
            });

            if (repeat) {
                renderer.processNodes(content);
            }
        },

        /**
         * Wraps provided string in curly braces if it's necessary.
         *
         * @param {String} args - String to be wrapped.
         * @returns {String} Wrapped string.
         */
        wrapArgs: function (args) {
            if (~args.indexOf('\\:')) {
                args = args.replace(colonReg, ':');
            } else if (~args.indexOf(':') && !~args.indexOf('}')) {
                args = '{' + args + '}';
            }

            return args;
        },

        /**
         * Wraps child nodes of provided DOM element
         * with knockout's comment tag.
         *
         * @param {HTMLElement} node - Node whose children should be wrapped.
         * @param {String} binding - Name of the binding for the opener comment tag.
         * @param {String} data - Data associated with a binding.
         *
         * @example
         *      <div id="example"><span/></div>
         *      wrapChildren(document.getElementById('example'), 'foreach', 'data');
         *      =>
         *      <div id="example">
         *      <!-- ko foreach: data -->
         *          <span></span>
         *      <!-- /ko -->
         *      </div>
         */
        wrapChildren: function (node, binding, data) {
            var tag = this.createComment(binding, data),
                $node = $(node);

            $node.prepend(tag.open);
            $node.append(tag.close);
        },

        /**
         * Wraps specified node with knockout's comment tag.
         *
         * @param {HTMLElement} node - Node to be wrapped.
         * @param {String} binding - Name of the binding for the opener comment tag.
         * @param {String} data - Data associated with a binding.
         *
         * @example
         *      <div id="example"></div>
         *      wrapNode(document.getElementById('example'), 'foreach', 'data');
         *      =>
         *      <!-- ko foreach: data -->
         *          <div id="example"></div>
         *      <!-- /ko -->
         */
        wrapNode: function (node, binding, data) {
            var tag = this.createComment(binding, data),
                $node = $(node);

            $node.before(tag.open);
            $node.after(tag.close);
        },

        /**
         * Creates knockouts' comment tag for the provided binding.
         *
         * @param {String} binding - Name of the binding.
         * @param {String} data - Data associated with a binding.
         * @returns {Object} Object with an open and close comment elements.
         */
        createComment: function (binding, data) {
            return {
                open: document.createComment(' ko ' + binding + ': ' + data + ' '),
                close: document.createComment(' /ko ')
            };
        }
    };

    renderer.handlers = {

        /**
         * Basic node handler. Replaces custom nodes
         * with a corresponding knockout's comment tag.
         *
         * @param {HTMLElement} node - Node to be processed.
         * @param {String} data
         * @param {Object} element
         * @returns {Boolean} True
         *
         * @example Sample syntaxes conversions.
         *      <with args="model">
         *          <span/>
         *      </with>
         *      =>
         *      <!-- ko with: model-->
         *          <span/>
         *      <!-- /ko -->
         */
        node: function (node, data, element) {
            data = renderer.wrapArgs(data);

            renderer.wrapNode(node, element.binding, data);
            $(node).replaceWith(node.childNodes);

            return true;
        },

        /**
         * Base attribute handler. Replaces custom attributes with
         * a corresponding knockouts' data binding.
         *
         * @param {HTMLElement} node - Node to be processed.
         * @param {String} data - Data associated with a binding.
         * @param {Object} attr - Attribute definition.
         *
         * @example Sample syntaxes conversions.
         *      <div text="label"></div>
         *      =>
         *      <div data-bind="text: label"></div>
         */
        attribute: function (node, data, attr) {
            data = renderer.wrapArgs(data);

            renderer.bindings.add(node, attr.binding, data);
            node.removeAttribute(attr.name);
        },

        /**
         * Wraps provided node with a knockouts' comment tag.
         *
         * @param {HTMLElement} node - Node that will be wrapped.
         * @param {String} data - Data associated with a binding.
         * @param {Object} attr - Attribute definition.
         *
         * @example
         *      <div outereach="data" class="test"></div>
         *      =>
         *      <!-- ko foreach: data -->
         *          <div class="test"></div>
         *      <!-- /ko -->
         */
        wrapAttribute: function (node, data, attr) {
            data = renderer.wrapArgs(data);

            renderer.wrapNode(node, attr.binding, data);
            node.removeAttribute(attr.name);
        }
    };

    renderer.bindings = {

        /**
         * Appends binding string to the current
         * 'data-bind' attribute of provided node.
         *
         * @param {HTMLElement} node - DOM element whose 'data-bind' attribute will be extended.
         * @param {String} name - Name of a binding.
         * @param {String} data - Data associated with the binding.
         */
        add: function (node, name, data) {
            var bindings = this.get(node);

            if (bindings) {
                bindings += ', ';
            }

            bindings += name;

            if (data) {
                bindings += ': ' + data;
            }

            this.set(node, bindings);
        },

        /**
         * Extracts value of a 'data-bind' attribute from provided node.
         *
         * @param {HTMLElement} node - Node whose attribute to be extracted.
         * @returns {String}
         */
        get: function (node) {
            return node.getAttribute('data-bind') || '';
        },

        /**
         * Sets 'data-bind' attribute of the specified node
         * to the provided value.
         *
         * @param {HTMLElement} node - Node whose attribute will be altered.
         * @param {String} bindings - New value of 'data-bind' attribute.
         */
        set: function (node, bindings) {
            node.setAttribute('data-bind', bindings);
        }
    };

    renderer
        .addGlobal(renderer.processAttributes)
        .addGlobal(renderer.processNodes);

    /**
     * Collection of default binding conversions.
     */
    preset = {
        nodes: _.object([
            'if',
            'text',
            'with',
            'scope',
            'ifnot',
            'foreach',
            'component'
        ], Array.prototype),
        attributes: _.object([
            'css',
            'attr',
            'html',
            'with',
            'text',
            'click',
            'event',
            'submit',
            'enable',
            'disable',
            'options',
            'visible',
            'template',
            'hasFocus',
            'textInput',
            'component',
            'uniqueName',
            'optionsText',
            'optionsValue',
            'checkedValue',
            'selectedOptions'
        ], Array.prototype)
    };

    _.extend(preset.attributes, {
        if: renderer.handlers.wrapAttribute,
        ifnot: renderer.handlers.wrapAttribute,
        innerif: {
            binding: 'if'
        },
        innerifnot: {
            binding: 'ifnot'
        },
        outereach: {
            binding: 'foreach',
            handler: renderer.handlers.wrapAttribute
        },
        foreach: {
            name: 'each'
        },
        value: {
            name: 'ko-value'
        },
        style: {
            name: 'ko-style'
        },
        checked: {
            name: 'ko-checked'
        },
        disabled: {
            name: 'ko-disabled',
            binding: 'disable'
        },
        focused: {
            name: 'ko-focused',
            binding: 'hasFocus'
        },

        /**
         * Custom 'render' attribute handler function. Wraps child elements
         * of a node with knockout's 'ko template:' comment tag.
         *
         * @param {HTMLElement} node - Element to be processed.
         * @param {String} data - Data specified in 'render' attribute of a node.
         */
        render: function (node, data) {
            data = data || 'getTemplate()';
            data = renderer.wrapArgs(data);

            renderer.wrapChildren(node, 'template', data);
            node.removeAttribute('render');
        }
    });

    _.extend(preset.nodes, {
        foreach: {
            name: 'each'
        },

        /**
         * Custom 'render' node handler function.
         * Replaces node with knockout's 'ko template:' comment tag.
         *
         * @param {HTMLElement} node - Element to be processed.
         * @param {String} data - Data specified in 'args' attribute of a node.
         */
        render: function (node, data) {
            data = data || 'getTemplate()';
            data = renderer.wrapArgs(data);

            renderer.wrapNode(node, 'template', data);
            $(node).replaceWith(node.childNodes);
        }
    });

    _.each(preset.attributes, function (data, id) {
        renderer.addAttribute(id, data);
    });

    _.each(preset.nodes, function (data, id) {
        renderer.addNode(id, data);
    });

    return renderer;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/levels-pool',[
    'underscore'
], function (_) {
    'use strict';

    var LEVELS,
        CODE_MAP;

    LEVELS = {
        NONE: 0,
        ERROR: 1,
        WARN: 2,
        INFO: 3,
        DEBUG: 4,
        ALL: 5
    };

    CODE_MAP = _.invert(LEVELS);

    return {
        /**
         * Returns the list of available log levels.
         *
         * @returns {Object}
         */
        getLevels: function () {
            return LEVELS;
        },

        /**
         * Returns name of the log level that matches to the provided code.
         *
         * @returns {String}
         */
        getNameByCode: function (code) {
            return CODE_MAP[code];
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/logger',[
    './levels-pool'
], function (logLevels) {
    'use strict';

    var levels = logLevels.getLevels();

    /**
     * @param {LogOutputHandler} outputHandler
     * @param {LogEntryFactory} entryFactory
     */
    function Logger(outputHandler, entryFactory) {
        /**
         * An array of log entries.
         *
         * @protected
         * @type {Array<LogEntry>}
         */
        this.entries_ = [];

        /**
         * Current display level.
         *
         * @protected
         * @type {Number}
         */
        this.displayLevel_ = levels.ERROR;

        /**
         * An array of display criteria.
         *
         * @protected
         * @type {Array<LogCriteria>}
         */
        this.displayCriteria_ = [];

        /**
         * @protected
         * @type {LogEntryFactory}
         */
        this.entryFactory_ = entryFactory;

        /**
         * @protected
         * @type {Array<LogOutputHandler>}
         */
        this.outputHandlers_ = [outputHandler];

        this.addDisplayCriteria(this.matchesLevel_);
    }

    /**
     * Swaps current display level with the provided one.
     *
     * @param {Number} level - Level's code.
     */
    Logger.prototype.setDisplayLevel = function (level) {
        var levelName = logLevels.getNameByCode(level);

        if (!levelName) {
            throw new TypeError('The provided level is not defined in the levels list.');
        }

        this.displayLevel_ = level;
    };

    /**
     * Sets up the criteria by which log entries will be filtered out from the output.
     *
     * @param {LogCriteria} criteria
     */
    Logger.prototype.addDisplayCriteria = function (criteria) {
        this.displayCriteria_.push(criteria);
    };

    /**
     * Removes previously defined criteria.
     *
     * @param {LogCriteria} criteria
     */
    Logger.prototype.removeDisplayCriteria = function (criteria) {
        var index = this.displayCriteria_.indexOf(criteria);

        if (~index) {
            this.displayCriteria_.splice(index, 1);
        }
    };

    /**
     * @param {String} message
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.error = function (message, messageData) {
        return this.log_(message, levels.ERROR, messageData);
    };

    /**
     * @param {String} message
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.warn = function (message, messageData) {
        return this.log_(message, levels.WARN, messageData);
    };

    /**
     * @param {String} message
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.info = function (message, messageData) {
        return this.log_(message, levels.INFO, messageData);
    };

    /**
     * @param {String} message
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.debug = function (message, messageData) {
        return this.log_(message, levels.DEBUG, messageData);
    };

    /**
     * @protected
     * @param {String} message
     * @param {Number} level
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.log_ = function (message, level, messageData) {
        var entry = this.createEntry_(message, level, messageData);

        this.entries_.push(entry);

        if (this.matchesCriteria_(entry)) {
            this.processOutput_(entry);
        }

        return entry;
    };

    /**
     * @protected
     * @param {String} message
     * @param {Number} level
     * @param {Object} [messageData]
     * @returns {LogEntry}
     */
    Logger.prototype.createEntry_ = function (message, level, messageData) {
        return this.entryFactory_.createEntry(message, level, messageData);
    };

    /**
     * Returns an array of log entries that have been added to the logger.
     *
     * @param {LogCriteria} [criteria] - Optional filter criteria.
     * @returns {Array<LogEntry>}
     */
    Logger.prototype.getEntries = function (criteria) {
        if (criteria) {
            return this.entries_.filter(criteria);
        }

        return this.entries_;
    };

    /**
     * @param {LogCriteria} [criteria]
     */
    Logger.prototype.dump = function (criteria) {
        var entries;

        if (!criteria) {
            criteria = this.matchesCriteria_;
        }

        entries = this.entries_.filter(criteria, this);

        this.outputHandlers_.forEach(function (handler) {
            handler.dump(entries);
        });
    };

    /**
     * @protected
     * @param {LogEntry} entry
     */
    Logger.prototype.processOutput_ = function (entry) {
        this.outputHandlers_.forEach(function (handler) {
            handler.show(entry);
        });
    };

    /**
     * @protected
     * @param {LogEntry} entry
     * @returns {Boolean}
     */
    Logger.prototype.matchesCriteria_ = function (entry) {
        return this.displayCriteria_.every(function (criteria) {
            return criteria.call(this, entry);
        }, this);
    };

    /**
     * Checks that the level of provided entry passes the "displayLevel_" threshold.
     *
     * @protected
     * @param {LogEntry} entry - Entry to be checked.
     * @returns {Boolean}
     */
    Logger.prototype.matchesLevel_ = function (entry) {
        return entry.level <= this.displayLevel_;
    };

    return Logger;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/entry',[
    './levels-pool'
], function (logLevels) {
    'use strict';

    /**
     * @param {String} message
     * @param {Number} level
     * @param {Object} [data]
     */
    function LogEntry(message, level, data) {
        /**
         * @readonly
         * @type {Number}
         */
        this.timestamp = Date.now();

        /**
         * @readonly
         * @type {Number}
         */
        this.level = level;

        /**
         * @readonly
         * @type {String}
         */
        this.levelName = logLevels.getNameByCode(level);

        /**
         * @readonly
         * @type {Object}
         */
        this.data = data;

        /**
         * @readonly
         * @type {String}
         */
        this.message = message;
    }

    return LogEntry;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/entry-factory',[
    './entry'
], function (LogEntry) {
    'use strict';

    return {
        /**
         * @param {String} message
         * @param {Number} level
         * @param {Object} [messageData]
         * @returns {LogEntry}
         */
        createEntry: function (message, level, messageData) {
            return new LogEntry(message, level, messageData);
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/console-output-handler',[
    './levels-pool'
], function (logLevels) {
    'use strict';

    var levels = logLevels.getLevels();

    /**
     * @param {LogFormatter} formatter
     */
    function ConsoleOutputHandler(formatter) {
        /**
         * @protected
         * @type {LogFormatter}
         */
        this.formatter_ = formatter;
    }

    /**
     * Display data of the provided entry to the console.
     *
     * @param {LogEntry} entry - Entry to be displayed.
     */
    ConsoleOutputHandler.prototype.show = function (entry) {
        var displayString = this.formatter_.process(entry);

        switch (entry.level) {
            case levels.ERROR:
                console.error(displayString);
                break;

            case levels.WARN:
                console.warn(displayString);
                break;

            case levels.INFO:
                console.info(displayString);
                break;

            case levels.DEBUG:
                console.log(displayString);
                break;
        }
    };

    /**
     * Displays the array of entries.
     *
     * @param {Array<LogEntry>} entries
     */
    ConsoleOutputHandler.prototype.dump = function (entries) {
        entries.forEach(this.show, this);
    };

    return ConsoleOutputHandler;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/formatter',[
    'moment',
    'mage/utils/template'
], function (moment, mageTemplate) {
    'use strict';

    /**
     * @param {String} dateFormat
     * @param {String} template
     */
    function LogFormatter(dateFormat, template) {
        /**
         * @protected
         * @type {String}
         */
        this.dateFormat_ = 'YYYY-MM-DD hh:mm:ss';

        /**
         * @protected
         * @type {String}
         */
        this.template_ = '[${ $.date }] [${ $.entry.levelName }] ${ $.message }';

        if (dateFormat) {
            this.dateFormat_ = dateFormat;
        }

        if (template) {
            this.template_ = template;
        }
    }

    /**
     * @param {LogEntry} entry
     * @returns {String}
     */
    LogFormatter.prototype.process = function (entry) {
        var message = mageTemplate.template(entry.message, entry.data),
            date = moment(entry.timestamp).format(this.dateFormat_);

        return mageTemplate.template(this.template_, {
            date: date,
            entry: entry,
            message: message
        });
    };

    return LogFormatter;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/message-pool',[],function () {
    'use strict';

    var MESSAGES = {
        templateStartLoading:
            'The "${ $.template }" template requested by  the "${$.component}" component started loading.',
        templateLoadedFromServer:
            'The "${ $.template }" template requested by the "${$.component}" component  was loaded from server."',
        templateLoadedFromCache:
            'The "${ $.template }" template  requested by the "${$.component}" component was loaded from cache."',
        templateLoadingFail: 'Failed to load the "${ $.template }" template requested by "${$.component}".',
        componentStartInitialization:
            'Component "${$.component}" start initialization with instance name "${$.componentName}".',
        componentStartLoading: ' Started loading the "${$.component}" component.',
        componentFinishLoading: 'The "${$.component}" component was loaded.',
        componentLoadingFail: 'Failed to load the "${$.component}" component.',
        depsLoadingFail: 'Could not get the declared "${$.deps}" dependency for the "${$.component}" instance.',
        depsStartRequesting: 'Requesting the "${$.deps}" dependency for the "${$.component}" instance.',
        depsFinishRequesting: 'The "${$.deps}" dependency for the "${$.component}" instance was received.',
        requestingComponent: 'Requesting the "${$.component}" component.',
        requestingComponentIsLoaded: 'The requested "${$.component}" component was received.',
        requestingComponentIsFailed: 'Could not get the requested "${$.component}" component.'
    };

    return {
        /**
         * Returns message that matches the provided code.
         *
         * @param {String} code - Message's identifier
         * @returns {String}
         */
        getMessage: function (code) {
            return MESSAGES[code];
        },

        /**
         * Adds a new message to the poll.
         *
         * @param {String} code - Message's identifier.
         * @param {String} message - Text of the message
         */
        addMessage: function (code, message) {
            MESSAGES[code] = message;
        },

        /**
         * Tells whether message with provide code exists in the poll.
         *
         * @param {String} code - Message's identifier.
         * @returns {Boolean}
         */
        hasMessage: function (code) {
            return MESSAGES.hasOwnProperty(code);
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/logger-utils',[], function () {
    'use strict';

    /**
     * Utils methods for logger
     * @param {Logger} logger
     */
    function LogUtils(logger) {
        this.logger = logger;

    }

    /**
     * Method for logging asynchronous operations
     * @param {Promise} promise
     * @param {Object} config
     */
    LogUtils.prototype.asyncLog = function (promise, config) {
        var levels,
            messages,
            wait;

        config = config || {};
        levels = config.levels || this.createLevels();
        messages = config.messages || this.createMessages();
        wait = config.wait || 5000;

        this.logger[levels.requested](messages.requested, config.data);
        setTimeout(function () {
            promise.state() === 'pending' ?
                this.logger[levels.failed](messages.failed, config.data) :
                this.logger[levels.loaded](messages.loaded, config.data);
        }.bind(this), wait);
    };

    /**
     * Method that creates object of messages
     * @param {String} requested - log message that showing that request for class is started
     * @param {String} loaded - log message that show when requested class is loaded
     * @param {String} failed - log message that show when requested class is failed
     * @returns {Object}
     */
    LogUtils.prototype.createMessages = function (requested, loaded, failed) {
        return {
            requested: requested || '',
            loaded: loaded || '',
            failed: failed || ''
        };
    };

    /**
     * Method that creates object of log levels
     * @param {String} requested - log message that showing that request for class is started
     * @param {String} loaded - log message that show when requested class is loaded
     * @param {String} failed - log message that show when requested class is failed
     * @returns {Object}
     */
    LogUtils.prototype.createLevels = function (requested, loaded, failed) {
        return {
            requested: requested || 'info',
            loaded: loaded || 'info',
            failed: failed || 'warn'
        };
    };

    return LogUtils;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/logger/console-logger',[
    './logger',
    './entry-factory',
    './console-output-handler',
    './formatter',
    './message-pool',
    './levels-pool',
    'Magento_Ui/js/lib/core/storage/local',
    'underscore',
    './logger-utils'
], function (Logger, entryFactory, ConsoleHandler, Formatter, messagePoll, levelsPoll, storage, _, LoggerUtils) {
    'use strict';

    var STORAGE_NAMESPACE = 'CONSOLE_LOGGER';

    /**
     * Singleton Logger's sub-class instance of which is configured to display its
     * messages to the console. It also provides the support of predefined messages
     * and persists its display level.
     */
    function ConsoleLogger() {
        var formatter = new Formatter(),
            consoleHandler = new ConsoleHandler(formatter),
            savedLevel = storage.get(STORAGE_NAMESPACE),
            utils = new LoggerUtils(this);

        Logger.call(this, consoleHandler, entryFactory);

        if (savedLevel) {
            this.displayLevel_ = savedLevel;
        }

        this.utils = utils;
        this.messages = messagePoll;
        this.levels = levelsPoll.getLevels();
    }

    _.extend(ConsoleLogger, Logger);

    ConsoleLogger.prototype = Object.create(Logger.prototype);
    ConsoleLogger.prototype.constructor = ConsoleLogger;

    /**
     * Overrides parent method to save the provided display level.
     *
     * @override
     */
    ConsoleLogger.prototype.setDisplayLevel = function (level) {
        Logger.prototype.setDisplayLevel.call(this, level);

        storage.set(STORAGE_NAMESPACE, level);
    };

    /**
     * Adds the support of predefined messages.
     *
     * @protected
     * @override
     */
    ConsoleLogger.prototype.createEntry_ = function (message, level, data) {
        var code;

        if (messagePoll.hasMessage(message)) {
            data = data || {};
            code = message;
            message = messagePoll.getMessage(code);

            data.messageCode = code;
        }

        return Logger.prototype.createEntry_.call(this, message, level, data);
    };

    return new ConsoleLogger();
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/template/engine',[
    'jquery',
    'ko',
    'underscore',
    './observable_source',
    './renderer',
    '../../logger/console-logger'
], function ($, ko, _, Source, renderer, consoleLogger) {
    'use strict';

    var RemoteTemplateEngine,
        NativeTemplateEngine = ko.nativeTemplateEngine,
        sources = {};

    /**
     * Remote template engine class. Is used to be able to load remote templates via knockout template binding.
     */
    RemoteTemplateEngine = function () {
        // Instance reference for closure.
        var engine = this,
        // Decorate the builtin Knockout "template" binding to track synchronous template renders.
        origUpdate = ko.bindingHandlers.template.update;

        /**
         * Counter to track the number of currently running render tasks (both synchronous and asynchronous).
         * @type {Number}
         * @private
         */
        this._rendersOutstanding = 0;

        /**
         * Use a jQuery object as an event bus (but any event emitter with on/off/emit methods could work)
         * @type {jQuery}
         * @private
         */
        this._events = $(this);

        /**
         * Rendered templates
         * @type {Object}
         * @private
         */
        this._templatesRendered = {};

        /*eslint-disable no-unused-vars*/
        /**
         * Decorate update method
         *
         * @param {HTMLElement} element
         * @param {Function} valueAccessor
         * @param {Object} allBindings
         * @param {Object} viewModel
         * @param {ko.bindingContext} bindingContext
         * @returns {*}
         */
        ko.bindingHandlers.template.update = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            /*eslint-enable no-unused-vars*/
            var options = ko.utils.peekObservable(valueAccessor()),
                templateName,
                isSync,
                updated;

            if (typeof options === 'object') {
                if (options.templateEngine && options.templateEngine !== engine) {
                    return origUpdate.apply(this, arguments);
                }

                if (!options.name) {
                    consoleLogger.error('Could not find template name', options);
                }
                templateName = options.name;
            } else if (typeof options === 'string') {
                templateName = options;
            } else {
                consoleLogger.error('Could not build a template binding', options);
            }
            engine._trackRender(templateName);
            isSync = engine._hasTemplateLoaded(templateName);
            updated = origUpdate.apply(this, arguments);

            if (isSync) {
                engine._releaseRender(templateName, 'sync');
            }

            return updated;
        };
    };

    /**
     * Creates unique template identifier based on template name and it's extenders (optional)
     * @param  {String} templateName
     * @return {String} - unique template identifier
     */
    function createTemplateIdentifier(templateName) {
        return templateName;
    }

    RemoteTemplateEngine.prototype = new NativeTemplateEngine;
    RemoteTemplateEngine.prototype.constructor = RemoteTemplateEngine;

    /**
     * When an asynchronous render task begins, increment the internal counter for tracking when renders are complete.
     * @private
     */
    RemoteTemplateEngine.prototype._trackRender = function (templateName) {
        var rendersForTemplate = this._templatesRendered[templateName] !== undefined ?
            this._templatesRendered[templateName] : 0;

        this._rendersOutstanding++;
        this._templatesRendered[templateName] = rendersForTemplate + 1;
        this._resolveRenderWaits();
    };

    /**
     * When an asynchronous render task ends, decrement the internal counter for tracking when renders are complete.
     * @private
     */
    RemoteTemplateEngine.prototype._releaseRender = function (templateName) {
        var rendersForTemplate = this._templatesRendered[templateName];

        this._rendersOutstanding--;
        this._templatesRendered[templateName] = rendersForTemplate - 1;
        this._resolveRenderWaits();
    };

    /**
     * Check to see if renders are complete and trigger events for listeners.
     * @private
     */
    RemoteTemplateEngine.prototype._resolveRenderWaits = function () {
        if (this._rendersOutstanding === 0) {
            this._events.triggerHandler('finishrender');
        }
    };

    /**
     * Get a promise for the end of the current run of renders, both sync and async.
     * @return {jQueryPromise} - promise that resolves when render completes
     */
    RemoteTemplateEngine.prototype.waitForFinishRender = function () {
        var defer = $.Deferred();

        this._events.one('finishrender', defer.resolve);

        return defer.promise();
    };

    /**
     * Returns true if this template has already been asynchronously loaded and will be synchronously rendered.
     * @param {String} templateName
     * @returns {Boolean}
     * @private
     */
    RemoteTemplateEngine.prototype._hasTemplateLoaded = function (templateName) {
        // Sources object will have cached template once makeTemplateSource has run
        return sources.hasOwnProperty(templateName);
    };

    /**
     * Overrided method of native knockout template engine.
     * Caches template after it's unique name and renders in once.
     * If template name is not typeof string, delegates work to knockout.templateSources.anonymousTemplate.
     * @param  {*} template
     * @param  {HTMLElement} templateDocument - document
     * @param  {Object} options - options, passed to template binding
     * @param  {ko.bindingContext} bindingContext
     * @returns {TemplateSource} Object with methods 'nodes' and 'data'.
     */
    RemoteTemplateEngine.prototype.makeTemplateSource = function (template, templateDocument, options, bindingContext) {
        var engine = this,
            source,
            templateId;

        if (typeof template === 'string') {
            templateId = createTemplateIdentifier(template);
            source = sources[templateId];

            if (!source) {
                source = new Source(template);
                source.requestedBy = bindingContext.$data.name;
                sources[templateId] = source;

                consoleLogger.info('templateStartLoading', {
                    template: templateId,
                    component: bindingContext.$data.name
                });

                renderer.render(template).then(function (rendered) {
                    consoleLogger.info('templateLoadedFromServer', {
                        template: templateId,
                        component: bindingContext.$data.name
                    });
                    source.nodes(rendered);
                    engine._releaseRender(templateId, 'async');
                }).fail(function () {
                    consoleLogger.error('templateLoadingFail', {
                        template: templateId,
                        component: bindingContext.$data.name
                    });
                });
            }

            if (source.requestedBy !== bindingContext.$data.name) {
                consoleLogger.info('templateLoadedFromCache', {
                    template: templateId,
                    component: bindingContext.$data.name
                });
            }

            return source;
        } else if (template.nodeType === 1 || template.nodeType === 8) {
            source = new ko.templateSources.anonymousTemplate(template);

            return source;
        }

        throw new Error('Unknown template type: ' + template);
    };

    /**
     * Overrided method of native knockout template engine.
     * Should return array of html elements.
     * @param  {TemplateSource} templateSource - object with methods 'nodes' and 'data'.
     * @return {Array} - array of html elements
     */
    RemoteTemplateEngine.prototype.renderTemplateSource = function (templateSource) {
        var nodes = templateSource.nodes();

        return ko.utils.cloneNodes(nodes);
    };

    /**
     * Overrided method of native knockout template engine.
     * Created in order to invoke makeTemplateSource method with custom set of params.
     * @param  {*} template - template identifier
     * @param  {ko.bindingContext} bindingContext
     * @param  {Object} options - options, passed to template binding
     * @param  {HTMLElement} templateDocument - document
     * @return {Array} - array of html elements
     */
    RemoteTemplateEngine.prototype.renderTemplate = function (template, bindingContext, options, templateDocument) {
        var templateSource = this.makeTemplateSource(template, templateDocument, options, bindingContext);

        return this.renderTemplateSource(templateSource);
    };

    return new RemoteTemplateEngine;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/extender/bound-nodes',[
    'ko',
    'underscore',
    'mage/utils/wrapper',
    'uiEvents'
], function (ko, _, wrapper, Events) {
    'use strict';

    var nodesMap = new WeakMap();

    /**
     * Returns a array of nodes associated with a specified model.
     *
     * @param {Object} model
     * @returns {Undefined|Array}
     */
    function getBounded(model) {
        return nodesMap.get(model);
    }

    /**
     * Removes specified node to models' associations list, if it's
     * a root node (node is not a descendant of any previously added nodes).
     * Triggers 'addNode' event.
     *
     * @param {Object} model
     * @param {HTMLElement} node
     */
    function addBounded(model, node) {
        var nodes = getBounded(model),
            isRoot;

        if (!nodes) {
            nodesMap.set(model, [node]);

            Events.trigger.call(model, 'addNode', node);

            return;
        }

        isRoot = nodes.every(function (bounded) {
            return !bounded.contains(node);
        });

        if (isRoot) {
            nodes.push(node);

            Events.trigger.call(model, 'addNode', node);
        }
    }

    /**
     * Removes specified node from models' associations list.
     * Triggers 'removeNode' event.
     *
     * @param {Object} model
     * @param {HTMLElement} node
     */
    function removeBounded(model, node) {
        var nodes = getBounded(model),
            index;

        if (!nodes) {
            return;
        }

        index = nodes.indexOf(node);

        if (~index) {
            nodes.splice(index, 0);

            Events.trigger.call(model, 'removeNode', node);
        }

        if (!nodes.length) {
            nodesMap.delete(model);
        }
    }

    /**
     * Returns node's first sibling of 'element' type within the common component scope
     *
     * @param {HTMLElement} node
     * @param {*} data
     * @returns {HTMLElement}
     */
    function getElement(node, data) {
        var elem;

        while (node.nextElementSibling) {
            node = node.nextElementSibling;

            if (node.nodeType === 1 && ko.dataFor(node) === data) {
                elem = node;
                break;
            }
        }

        return elem;
    }

    wrapper.extend(ko, {

        /**
         * Extends knockouts' 'applyBindings'
         * to track nodes associated with model.
         *
         * @param {Function} orig - Original 'applyBindings' method.
         * @param {Object} ctx
         * @param {HTMLElement} node - Original 'applyBindings' method.
         */
        applyBindings: function (orig, ctx, node) {
            var result = orig(),
                data = ctx && (ctx.$data || ctx);

            if (node && node.nodeType === 8) {
                node = getElement(node, data);
            }

            if (!node || node.nodeType !== 1) {
                return result;
            }

            if (data && data.registerNodes) {
                addBounded(data, node);
            }

            return result;
        },

        /**
         * Extends knockouts' cleanNode
         * to track nodes associated with model.
         *
         * @param {Function} orig - Original 'cleanNode' method.
         * @param {HTMLElement} node - Original 'cleanNode' method.
         */
        cleanNode: function (orig, node) {
            var result = orig(),
                data;

            if (node.nodeType !== 1) {
                return result;
            }

            data = ko.dataFor(node);

            if (data && data.registerNodes) {
                removeBounded(data, node);
            }

            return result;
        }
    });

    return {

        /**
         * Returns root nodes associated with a model. If callback is provided,
         * will iterate through all of the present nodes triggering callback
         * for each of it. Also it will subscribe to the 'addNode' event.
         *
         * @param {Object} model
         * @param {Function} [callback]
         * @returns {Array|Undefined}
         */
        get: function (model, callback) {
            var nodes = getBounded(model) || [];

            if (!_.isFunction(callback)) {
                return nodes;
            }

            nodes.forEach(function (node) {
                callback(node);
            });

            this.add.apply(this, arguments);
        },

        /**
         * Subscribes to adding of nodes associated with a model.
         *
         * @param {Object} model
         */
        add: function (model) {
            var args = _.toArray(arguments).slice(1);

            args.unshift('addNode');

            Events.on.apply(model, args);
        },

        /**
         * Subscribes to removal of nodes associated with a model.
         *
         * @param {Object} model
         */
        remove: function (model) {
            var args = _.toArray(arguments).slice(1);

            args.unshift('removeNode');

            Events.on.apply(model, args);
        },

        /**
         * Removes subscriptions from the model.
         *
         * @param {Object} model
         */
        off: function (model) {
            var args = _.toArray(arguments).slice(1);

            Events.off.apply(model, args);
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/view/utils/bindings',[
    'ko',
    'jquery',
    'underscore'
], function (ko, $, _) {
    'use strict';

    /**
     * Checks if provided  value is a dom element.
     *
     * @param {*} node - Value to be checked.
     * @returns {Boolean}
     */
    function isDomElement(node) {
        return typeof node === 'object' && node.tagName && node.nodeType;
    }

    /**
     * Removes from the provided array all non-root nodes located inside
     * of the comment element as long as the closing comment tags.
     *
     * @param {(Array|ArrayLike)} nodes - An array of nodes to be processed.
     * @returns {Array}
     */
    function normalize(nodes) {
        var result;

        nodes   = _.toArray(nodes);
        result  = nodes.slice();

        nodes.forEach(function (node) {
            if (node.nodeType === 8) {
                result = !ko.virtualElements.hasBindingValue(node) ?
                    _.without(result, node) :
                    _.difference(result, ko.virtualElements.childNodes(node));
            }
        });

        return result;
    }

    /**
     * Extends binding context of each item in the collection.
     *
     * @param {...Object} extenders - Multiple extender objects to be applied to the context.
     * @returns {jQueryCollection} Chainable.
     */
    $.fn.extendCtx = function () {
        var nodes       = normalize(this),
            extenders   = _.toArray(arguments);

        nodes.forEach(function (node) {
            var ctx  = ko.contextFor(node),
                data = [ctx].concat(extenders);

            _.extend.apply(_, data);
        });

        return this;
    };

    /**
     * Evaluates bindings specified in each DOM element of collection.
     *
     * @param {(HTMLElement|Object)} [ctx] - Context to use for bindings evaluation.
     *      If not specified then current context of a collections' item will be used.
     * @returns {jQueryCollection} Chainable.
     */
    $.fn.applyBindings = function (ctx) {
        var nodes = normalize(this),
            nodeCtx;

        if (isDomElement(ctx)) {
            ctx = ko.contextFor(ctx);
        }

        nodes.forEach(function (node) {
            nodeCtx = ctx || ko.contextFor(node);

            ko.applyBindings(nodeCtx, node);
        });

        return this;
    };

    /**
     * Adds specified bindings to each DOM element in
     * collection and evaluates them with provided context.
     *
     * @param {(Object|Function)} data - Either bindings object or a function
     *      which returns bindings data for each element in collection.
     * @param {(HTMLElement|Object)} [ctx] - Context to use for bindings evaluation.
     *      If not specified then current context of a collections' item will be used.
     * @returns {jQueryCollection} Chainable.
     */
    $.fn.bindings = function (data, ctx) {
        var nodes    = normalize(this),
            bindings = data,
            nodeCtx;

        if (isDomElement(ctx)) {
            ctx = ko.contextFor(ctx);
        }

        nodes.forEach(function (node) {
            nodeCtx = ctx || ko.contextFor(node);

            if (_.isFunction(data)) {
                bindings = data(nodeCtx, node);
            }

            ko.applyBindingsToNode(node, bindings, nodeCtx);
        });

        return this;
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/i18n',[
    'jquery',
    'ko',
    'module',
    '../template/renderer',
    'mage/translate'
], function ($, ko, module, renderer) {
    'use strict';

    var locations = {
            'legend': 'Caption for the fieldset element',
            'label': 'Label for an input element.',
            'button': 'Push button',
            'a': 'Link label',
            'b': 'Bold text',
            'strong': 'Strong emphasized text',
            'i': 'Italic text',
            'em': 'Emphasized text',
            'u': 'Underlined text',
            'sup': 'Superscript text',
            'sub': 'Subscript text',
            'span': 'Span element',
            'small': 'Smaller text',
            'big': 'Bigger text',
            'address': 'Contact information',
            'blockquote': 'Long quotation',
            'q': 'Short quotation',
            'cite': 'Citation',
            'caption': 'Table caption',
            'abbr': 'Abbreviated phrase',
            'acronym': 'An acronym',
            'var': 'Variable part of a text',
            'dfn': 'Term',
            'strike': 'Strikethrough text',
            'del': 'Deleted text',
            'ins': 'Inserted text',
            'h1': 'Heading level 1',
            'h2': 'Heading level 2',
            'h3': 'Heading level 3',
            'h4': 'Heading level 4',
            'h5': 'Heading level 5',
            'h6': 'Heading level 6',
            'center': 'Centered text',
            'select': 'List options',
            'img': 'Image',
            'input': 'Form element'
        },

        /**
         * Generates [data-translate] attribute's value
         * @param {Object} translationData
         * @param {String} location
         */
        composeTranslateAttr = function (translationData, location) {
            var obj = [{
                'shown': translationData.shown,
                'translated': translationData.translated,
                'original': translationData.original,
                'location': locations[location] || 'Text'
            }];

            return JSON.stringify(obj);
        },

        /**
         * Sets text for the element
         * @param {Object} el
         * @param {String} text
         */
        setText = function (el, text) {
            $(el).text(text);
        },

        /**
         * Sets [data-translate] attribute for the element
         * @param {Object} el - The element which is binded
         * @param {String} original - The original value of the element
         */
        setTranslateProp = function (el, original) {
            var location = $(el).prop('tagName').toLowerCase(),
                translated = $.mage.__(original),
                translationData = {
                    shown: translated,
                    translated: translated,
                    original: original
                },
                translateAttr = composeTranslateAttr(translationData, location);

            $(el).attr('data-translate', translateAttr);

            setText(el, translationData.shown);
        },

        /**
         * Checks if node represents ko virtual node (nodeType === 8, nodeName === '#comment').
         *
         * @param {HTMLElement} node
         * @returns {Boolean}
         */
        isVirtualElement = function (node) {
            return node.nodeType === 8;
        },

        /**
        * Checks if it's real DOM element
        * in case of virtual element, returns span wrapper
        * @param {Object} el
        * @param {bool} isUpdate
        * @return {Object} el
        */
        getRealElement = function (el, isUpdate) {
            if (isVirtualElement(el)) {
                if (isUpdate) {
                    return $(el).next('span');
                }

                return $('<span></span>').insertAfter(el);
            }

            return el;
        },

        /**
         * execute i18n binding
         * @param {Object} element
         * @param {Function} valueAccessor
         * @param {bool} isUpdate
         */
        execute = function (element, valueAccessor, isUpdate) {
            var original = ko.unwrap(valueAccessor() || ''),
                el = getRealElement(element, isUpdate),
                inlineTranslation = (module.config() || {}).inlineTranslation;

            if (inlineTranslation) {
                setTranslateProp(el, original);
            } else {
                setText(el, $.mage.__(original));
            }
        };

    /**
     * i18n binding
     * @property {Function}  init
     * @property {Function}  update
     */
    ko.bindingHandlers.i18n = {

        /**
         * init i18n binding
         * @param {Object} element
         * @param {Function} valueAccessor
         */
        init: function (element, valueAccessor) {
            execute(element, valueAccessor);
        },

        /**
         * update i18n binding
         * @param {Object} element
         * @param {Function} valueAccessor
         */
        update: function (element, valueAccessor) {
            execute(element, valueAccessor, true);
        }
    };

    ko.virtualElements.allowedBindings.i18n = true;

    renderer
        .addNode('translate', {
            binding: 'i18n'
        })
        .addAttribute('translate', {
            binding: 'i18n'
        });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/** Creates scope binding and registers in to ko.bindingHandlers object */
define('Magento_Ui/js/lib/knockout/bindings/scope',[
    'ko',
    'uiRegistry',
    'mage/translate',
    '../template/renderer',
    'jquery',
    '../../logger/console-logger'
], function (ko, registry, $t, renderer, $, consoleLogger) {
    'use strict';

    /**
     * Creates child context with passed component param as $data. Extends context with $t helper.
     * Applies bindings to descendant nodes.
     * @param {HTMLElement} el - element to apply bindings to.
     * @param {ko.bindingContext} bindingContext - instance of ko.bindingContext, passed to binding initially.
     * @param {Promise} promise - instance of jQuery promise
     * @param {Object} component - component instance to attach to new context
     */
    function applyComponents(el, bindingContext, promise, component) {
        promise.resolve();
        component = bindingContext.createChildContext(component);

        ko.utils.extend(component, {
            $t: $t
        });

        ko.utils.arrayForEach(ko.virtualElements.childNodes(el), ko.cleanNode);

        ko.applyBindingsToDescendants(component, el);
    }

    ko.bindingHandlers.scope = {

        /**
         * Scope binding's init method.
         * @returns {Object} - Knockout declaration for it to let binding control descendants.
         */
        init: function () {
            return {
                controlsDescendantBindings: true
            };
        },

        /**
         * Reads params passed to binding, parses component declarations.
         * Fetches for those found and attaches them to the new context.
         * @param {HTMLElement} el - Element to apply bindings to.
         * @param {Function} valueAccessor - Function that returns value, passed to binding.
         * @param {Object} allBindings - Object, which represents all bindings applied to element.
         * @param {Object} viewModel - Object, which represents view model binded to el.
         * @param {ko.bindingContext} bindingContext - Instance of ko.bindingContext, passed to binding initially.
         */
        update: function (el, valueAccessor, allBindings, viewModel, bindingContext) {
            var component = valueAccessor(),
                promise = $.Deferred(),
                apply = applyComponents.bind(this, el, bindingContext, promise),
                loggerUtils = consoleLogger.utils;

            if (typeof component === 'string') {
                loggerUtils.asyncLog(
                    promise,
                    {
                        data: {
                            component: component
                        },
                        messages: loggerUtils.createMessages(
                            'requestingComponent',
                            'requestingComponentIsLoaded',
                            'requestingComponentIsFailed'
                        )
                    }
                );

                registry.get(component, apply);
            } else if (typeof component === 'function') {
                component(apply);
            }
        }
    };

    ko.virtualElements.allowedBindings.scope = true;

    renderer
        .addNode('scope')
        .addAttribute('scope', {
            name: 'ko-scope'
        });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/range',[
    'ko',
    'jquery',
    'underscore',
    '../template/renderer'
], function (ko, $, _, renderer) {
    'use strict';

    var isTouchDevice = !_.isUndefined(document.ontouchstart),
        sliderFn = 'slider',
        sliderModule = 'jquery-ui-modules/slider';

    if (isTouchDevice) {
        sliderFn = 'touchSlider';
        sliderModule = 'mage/touch-slider';
    }

    ko.bindingHandlers.range = {

        /**
         * Initializes binding and a slider update.
         *
         * @param {HTMLElement} element
         * @param {Function} valueAccessor
         */
        init: function (element, valueAccessor) {
            var config  = valueAccessor(),
                value   = config.value;

            _.extend(config, {
                value: value(),

                /**
                 * Callback which is being called when sliders' value changes.
                 *
                 * @param {Event} event
                 * @param {Object} ui
                 */
                slide: function (event, ui) {
                    value(ui.value);
                }
            });

            require([sliderModule], function () {
                $(element)[sliderFn](config);
            });
        },

        /**
         * Updates sliders' plugin configuration.
         *
         * @param {HTMLElement} element
         * @param {Function} valueAccessor
         */
        update: function (element, valueAccessor) {
            var config = valueAccessor();

            config.value = ko.unwrap(config.value);

            require([sliderModule], function () {
                $(element)[sliderFn]('option', config);
            });
        }
    };

    renderer.addAttribute('range');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/mage-init',[
    'ko',
    'underscore',
    'mage/apply/main'
], function (ko, _, mage) {
    'use strict';

    ko.bindingHandlers.mageInit = {
        /**
         * Initializes components assigned to HTML elements.
         *
         * @param {HTMLElement} el
         * @param {Function} valueAccessor
         */
        init: function (el, valueAccessor) {
            var data = valueAccessor();

            _.each(data, function (config, component) {
                mage.applyFor(el, config, component);
            });
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/keyboard',[
    'ko',
    '../template/renderer'
], function (ko, renderer) {
    'use strict';

    ko.bindingHandlers.keyboard = {

        /**
         * Attaches keypress handlers to element
         * @param {HTMLElement} el - Element, that binding is applied to
         * @param {Function} valueAccessor - Function that returns value, passed to binding
         * @param  {Object} allBindings - all bindings object
         * @param  {Object} viewModel - reference to viewmodel
         */
        init: function (el, valueAccessor, allBindings, viewModel) {
            var map = valueAccessor();

            ko.utils.registerEventHandler(el, 'keyup', function (e) {
                var callback = map[e.keyCode];

                if (callback) {
                    return callback.call(viewModel, e);
                }
            });
        }
    };

    renderer.addAttribute('keyboard');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/optgroup',[
    'ko',
    'mageUtils'
    ], function (ko, utils) {
    'use strict';

    var captionPlaceholder = {},
        optgroupTmpl = '<optgroup label="${ $.label }"></optgroup>',
        nbspRe = /&nbsp;/g,
        optionsText,
        optionsValue,
        optionTitle;

    ko.bindingHandlers.optgroup = {
        /**
         * @param {*} element
         */
        init: function (element) {
            if (ko.utils.tagNameLower(element) !== 'select') {
                throw new Error('options binding applies only to SELECT elements');
            }

            // Remove all existing <option>s.
            while (element.length > 0) {
                element.remove(0);
            }
        },

        /**
         * @param {*} element
         * @param {*} valueAccessor
         * @param {*} allBindings
         */
        update: function (element, valueAccessor, allBindings) {
            var selectWasPreviouslyEmpty = element.length === 0,
                previousScrollTop = !selectWasPreviouslyEmpty && element.multiple ? element.scrollTop : null,
                includeDestroyed = allBindings.get('optionsIncludeDestroyed'),
                arrayToDomNodeChildrenOptions = {},
                captionValue,
                unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),
                filteredArray,
                previousSelectedValues,
                itemUpdate = false,
                callback = setSelectionCallback,//eslint-disable-line no-use-before-define
                nestedOptionsLevel = -1;

            optionsText = ko.utils.unwrapObservable(allBindings.get('optionsText')) || 'text';
            optionsValue = ko.utils.unwrapObservable(allBindings.get('optionsValue')) || 'value';
            optionTitle = optionsText + 'title';

            if (element.multiple) {
                previousSelectedValues = ko.utils.arrayMap(
                    selectedOptions(),//eslint-disable-line no-use-before-define
                    ko.selectExtensions.readValue
                );
            } else {
                previousSelectedValues = element.selectedIndex >= 0 ?
                    [ko.selectExtensions.readValue(element.options[element.selectedIndex])] :
                    [];
            }

            if (unwrappedArray) {
                if (typeof unwrappedArray.length === 'undefined') { // Coerce single value into array
                    unwrappedArray = [unwrappedArray];
                }

                // Filter out any entries marked as destroyed
                filteredArray = ko.utils.arrayFilter(unwrappedArray, function (item) {
                    if (item && !item.label) {
                        return false;
                    }

                    return includeDestroyed ||
                        item === undefined ||
                        item === null ||
                        !ko.utils.unwrapObservable(item._destroy);
                });
                filteredArray.map(recursivePathBuilder, null);//eslint-disable-line no-use-before-define
            }

            /**
             * @param {*} option
             */
            arrayToDomNodeChildrenOptions.beforeRemove = function (option) {
                element.removeChild(option);
            };

            if (allBindings.has('optionsAfterRender')) {

                /**
                 * @param {*} arrayEntry
                 * @param {*} newOptions
                 */
                callback = function (arrayEntry, newOptions) {
                    setSelectionCallback(arrayEntry, newOptions);//eslint-disable-line no-use-before-define
                    ko.dependencyDetection.ignore(
                        allBindings.get('optionsAfterRender'),
                        null,
                        [newOptions[0],
                        arrayEntry !== captionPlaceholder ? arrayEntry : undefined]
                    );
                };
            }

            filteredArray = formatOptions(filteredArray);//eslint-disable-line no-use-before-define
            ko.utils.setDomNodeChildrenFromArrayMapping(
                element,
                filteredArray,
                optionNodeFromArray,//eslint-disable-line no-use-before-define
                arrayToDomNodeChildrenOptions,
                callback
            );

            ko.dependencyDetection.ignore(function () {
                var selectionChanged;

                if (allBindings.get('valueAllowUnset') && allBindings.has('value')) {
                    // The model value is authoritative, so make sure its value is the one selected
                    ko.selectExtensions.writeValue(
                        element,
                        ko.utils.unwrapObservable(allBindings.get('value')),
                        true /* allowUnset */
                    );
                } else {
                    // Determine if the selection has changed as a result of updating the options list
                    if (element.multiple) {
                        // For a multiple-select box, compare the new selection count to the previous one
                        // But if nothing was selected before, the selection can't have changed
                        selectionChanged = previousSelectedValues.length &&
                            selectedOptions().length < //eslint-disable-line no-use-before-define
                            previousSelectedValues.length;
                    } else {
                        // For a single-select box, compare the current value to the previous value
                        // But if nothing was selected before or nothing is selected now,
                        // just look for a change in selection
                        selectionChanged = previousSelectedValues.length && element.selectedIndex >= 0 ?
                            ko.selectExtensions.readValue(element.options[element.selectedIndex]) !==
                            previousSelectedValues[0] : previousSelectedValues.length || element.selectedIndex >= 0;
                    }

                    // Ensure consistency between model value and selected option.
                    // If the dropdown was changed so that selection is no longer the same,
                    // notify the value or selectedOptions binding.
                    if (selectionChanged) {
                        ko.utils.triggerEvent(element, 'change');
                    }
                }
            });

            /*eslint-enable max-len, no-use-before-define*/

            if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20) {
                element.scrollTop = previousScrollTop;
            }

            /**
             * @returns {*}
             */
            function selectedOptions() {
                return ko.utils.arrayFilter(element.options, function (node) {
                    return node.selected;
                });
            }

            /**
             * @param {*} object
             * @param {*} predicate
             * @param {*} defaultValue
             * @returns {*}
             */
            function applyToObject(object, predicate, defaultValue) {
                var predicateType = typeof predicate;

                if (predicateType === 'function') {   // run it against the data value
                    return predicate(object);
                } else if (predicateType === 'string') { // treat it as a property name on the data value
                    return object[predicate];
                }

                return defaultValue;
            }

            /**
             * @param {*} obj
             */
            function recursivePathBuilder(obj) {

                obj[optionTitle] = (this && this[optionTitle] ? this[optionTitle] + '/' : '') + obj[optionsText].trim();

                if (Array.isArray(obj[optionsValue])) {
                    obj[optionsValue].map(recursivePathBuilder, obj);
                }
            }

            /**
             * @param {Array} arrayEntry
             * @param {*} oldOptions
             * @returns {*[]}
             */
            function optionNodeFromArray(arrayEntry, oldOptions) {
                var option;

                if (oldOptions.length) {
                    previousSelectedValues = oldOptions[0].selected ?
                        [ko.selectExtensions.readValue(oldOptions[0])] : [];
                    itemUpdate = true;
                }

                if (arrayEntry === captionPlaceholder) { // empty value, label === caption
                    option = element.ownerDocument.createElement('option');
                    ko.utils.setTextContent(option, allBindings.get('optionsCaption'));
                    ko.selectExtensions.writeValue(option, undefined);
                } else if (typeof arrayEntry[optionsValue] === 'undefined') { // empty value === optgroup
                    if (arrayEntry.__disableTmpl) {
                        option = '<optgroup label="' + arrayEntry[optionsText] + '"></optgroup>';
                    } else {
                        option = utils.template(optgroupTmpl, {
                            label: arrayEntry[optionsText],
                            title: arrayEntry[optionsText + 'title']
                        });
                    }
                    option = ko.utils.parseHtmlFragment(option)[0];

                } else {
                    option = element.ownerDocument.createElement('option');
                    option.setAttribute('data-title', arrayEntry[optionsText + 'title']);
                    ko.selectExtensions.writeValue(option, arrayEntry[optionsValue]);
                    ko.utils.setTextContent(option, arrayEntry[optionsText]);
                }

                return [option];
            }

            /**
             * @param {*} newOptions
             */
            function setSelectionCallback(newOptions) {
                var isSelected;

                // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
                // That's why we first added them without selection. Now it's time to set the selection.
                if (previousSelectedValues.length && newOptions.value) {
                    isSelected = ko.utils.arrayIndexOf(
                        previousSelectedValues,
                        ko.selectExtensions.readValue(newOptions.value)
                    ) >= 0;

                    ko.utils.setOptionNodeSelectionState(newOptions.value, isSelected);

                    // If this option was changed from being selected during a single-item update, notify the change
                    if (itemUpdate && !isSelected) {
                        ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, 'change']);
                    }
                }
            }

            /**
             * @param {*} string
             * @param {Number} times
             * @returns {Array}
             */
            function strPad(string, times) {
                return new Array(times + 1).join(string);
            }

            /**
             * @param {*} options
             * @returns {Array}
             */
            function formatOptions(options) {
                var res = [];

                nestedOptionsLevel++;

                if (!nestedOptionsLevel) { // zero level
                    // If caption is included, add it to the array
                    if (allBindings.has('optionsCaption')) {
                        captionValue = ko.utils.unwrapObservable(allBindings.get('optionsCaption'));
                        // If caption value is null or undefined, don't show a caption
                        if (//eslint-disable-line max-depth
                            captionValue !== null &&
                            captionValue !== undefined &&
                            captionValue !== false
                        ) {
                            res.push(captionPlaceholder);
                        }
                    }
                }

                ko.utils.arrayForEach(options, function (option) {
                    var value = applyToObject(option, optionsValue, option),
                        label = applyToObject(option, optionsText, value) || '',
                        disabled = applyToObject(option, 'disabled', false) || false,
                        obj = {},
                        space = '\u2007\u2007\u2007';

                    obj[optionTitle] = applyToObject(option, optionsText + 'title', value);

                    if (disabled) {
                        obj.disabled = disabled;
                    }

                    if (option.hasOwnProperty('__disableTmpl')) {
                        obj.__disableTmpl = option.__disableTmpl;
                    }

                    label = label.replace(nbspRe, '').trim();

                    if (Array.isArray(value)) {
                        obj[optionsText] = strPad('&nbsp;', nestedOptionsLevel * 4) + label;
                        res.push(obj);
                        res = res.concat(formatOptions(value));
                    } else {
                        obj[optionsText] = strPad(space, nestedOptionsLevel * 2) + label;
                        obj[optionsValue] = value;
                        res.push(obj);
                    }
                });
                nestedOptionsLevel--;

                return res;
            }
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/after-render',[
    'ko',
    '../template/renderer'
], function (ko, renderer) {
    'use strict';

    ko.bindingHandlers.afterRender = {

        /**
         * Binding init callback.
         */
        init: function (element, valueAccessor, allBindings, viewModel) {
            var callback = valueAccessor();

            if (typeof callback === 'function') {
                callback.call(viewModel, element, viewModel);
            }
        }
    };

    renderer.addAttribute('afterRender');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/autoselect',[
    'ko',
    'jquery',
    '../template/renderer'
], function (ko, $, renderer) {
    'use strict';

    /**
     * 'Focus' event handler.
     *
     * @param {EventObject} e
     */
    function onFocus(e) {
        e.target.select();
    }

    ko.bindingHandlers.autoselect = {

        /**
         * Adds event handler which automatically
         * selects inputs' element text when field gets focused.
         */
        init: function (element, valueAccessor) {
            var enabled = ko.unwrap(valueAccessor());

            if (enabled !== false) {
                $(element).on('focus', onFocus);
            }
        }
    };

    renderer.addAttribute('autoselect');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/** Creates outerClick binding and registers in to ko.bindingHandlers object */
define('Magento_Ui/js/lib/knockout/bindings/outer_click',[
    'ko',
    'jquery',
    'underscore',
    '../template/renderer'
], function (ko, $, _, renderer) {
    'use strict';

    var defaults = {
        onlyIfVisible: true
    };

    /**
     * Checks if element sis visible.
     *
     * @param {Element} el
     * @returns {Boolean}
     */
    function isVisible(el) {
        var style = window.getComputedStyle(el),
            visibility = {
                display: 'none',
                visibility: 'hidden',
                opacity: '0'
            },
            visible = true;

        _.each(visibility, function (val, key) {
            if (style[key] === val) {
                visible = false;
            }
        });

        return visible;
    }

    /**
     * Document click handler which in case if event target is not
     * a descendant of provided container element,
     * invokes specified in configuration callback.
     *
     * @param {HTMLElement} container
     * @param {Object} config
     * @param {EventObject} e
     */
    function onOuterClick(container, config, e) {
        var target = e.target,
            callback = config.callback;

        if (container === target || container.contains(target)) {
            return;
        }

        if (config.onlyIfVisible) {
            if (!_.isNull(container.offsetParent) && isVisible(container)) {
                callback();
            }
        } else {
            callback();
        }
    }

    /**
     * Prepares configuration for the binding based
     * on a default properties and provided options.
     *
     * @param {(Object|Function)} [options={}]
     * @returns {Object}
     */
    function buildConfig(options) {
        var config = {};

        if (_.isFunction(options)) {
            options = {
                callback: options
            };
        } else if (!_.isObject(options)) {
            options = {};
        }

        return _.extend(config, defaults, options);
    }

    ko.bindingHandlers.outerClick = {

        /**
         * Initializes outer click binding.
         */
        init: function (element, valueAccessor) {
            var config = buildConfig(valueAccessor()),
                outerClick = onOuterClick.bind(null, element, config),
                isTouchDevice = typeof document.ontouchstart !== 'undefined';

            if (isTouchDevice) {
                $(document).on('touchstart', outerClick);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(document).off('touchstart', outerClick);
                });
            } else {
                $(document).on('click', outerClick);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(document).off('click', outerClick);
                });
            }
        }
    };

    renderer.addAttribute('outerClick');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/fadeVisible',[
    'jquery',
    'ko'
], function ($, ko) {
    'use strict';

    ko.bindingHandlers.fadeVisible = {
        /**
         * Initially set the element to be instantly visible/hidden depending on the value.
         *
         * @param {HTMLElement} element
         * @param {Function} valueAccessor
         */
        init: function (element, valueAccessor) {
            var value = valueAccessor();

            // Use "unwrapObservable" so we can handle values that may or may not be observable
            $(element).toggle(ko.unwrap(value));
        },

        /**
         * Whenever the value subsequently changes, slowly fade the element in or out.
         *
         * @param {HTMLElement} element
         * @param {Function} valueAccessor
         */
        update: function (element, valueAccessor) {
            var value = valueAccessor();

            ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut();
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/collapsible',[
    'ko',
    'jquery',
    'underscore',
    '../template/renderer'
], function (ko, $, _, renderer) {
    'use strict';

    var collapsible,
        defaults;

    defaults = {
        closeOnOuter: true,
        onTarget: false,
        openClass: '_active',
        as: '$collapsible'
    };

    collapsible = {

        /**
         * Sets 'opened' property to true.
         */
        open: function () {
            this.opened(true);
        },

        /**
         * Sets 'opened' property to false.
         */
        close: function () {
            this.opened(false);
        },

        /**
         * Toggles value of the 'opened' property.
         */
        toggle: function () {
            this.opened(!this.opened());
        }
    };

    /**
     * Document click handler which in case if event target is not
     * a descendant of provided container element, closes collapsible model.
     *
     * @param {HTMLElement} container
     * @param {Object} model
     * @param {EventObject} e
     */
    function onOuterClick(container, model, e) {
        var target = e.target;

        if (target !== container && !container.contains(target)) {
            model.close();
        }
    }

    /**
     * Creates 'css' binding which toggles
     * class specified in 'name' parameter.
     *
     * @param {Object} model
     * @param {String} name
     * @returns {Object}
     */
    function getClassBinding(model, name) {
        var binding = {};

        binding[name] = model.opened;

        return {
            css: binding
        };
    }

    /**
     * Prepares configuration for the binding based
     * on a default properties and provided options.
     *
     * @param {Object} [options={}]
     * @returns {Object} Complete instance configuration.
     */
    function buildConfig(options) {
        if (typeof options !== 'object') {
            options = {};
        }

        return _.extend({}, defaults, options);
    }

    ko.bindingHandlers.collapsible = {

        /**
         * Initializes 'collapsible' binding.
         */
        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
            var $collapsible = Object.create(collapsible),
                config = buildConfig(valueAccessor()),
                outerClick,
                bindings;

            _.bindAll($collapsible, 'open', 'close', 'toggle');

            $collapsible.opened = ko.observable(!!config.opened);

            bindingCtx[config.as] = $collapsible;

            if (config.closeOnOuter) {
                outerClick = onOuterClick.bind(null, element, $collapsible);

                $(document).on('click', outerClick);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(document).off('click', outerClick);
                });
            }

            if (config.openClass) {
                bindings = getClassBinding($collapsible, config.openClass);

                ko.applyBindingsToNode(element, bindings, bindingCtx);
            }

            if (config.onTarget) {
                $(element).on('click', $collapsible.toggle);
            }

            if (viewModel && _.isFunction(viewModel.on)) {
                viewModel.on({
                    close:          $collapsible.close,
                    open:           $collapsible.open,
                    toggleOpened:   $collapsible.toggle
                });
            }
        }
    };

    ko.bindingHandlers.closeCollapsible = {

        /**
         * Creates listener for the click event on provided DOM element,
         * which closes associated with it collapsible model.
         */
        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
            var name = valueAccessor() || defaults.as,
                $collapsible = bindingCtx[name];

            if ($collapsible) {
                $(element).on('click', $collapsible.close);
            }
        }
    };

    ko.bindingHandlers.openCollapsible = {

        /**
         * Creates listener for the click event on provided DOM element,
         * which opens associated with it collapsible model.
         */
        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
            var name = valueAccessor() || defaults.as,
                $collapsible = bindingCtx[name];

            if ($collapsible) {
                $(element).on('click', $collapsible.open);
            }
        }
    };

    ko.bindingHandlers.toggleCollapsible = {

        /**
         * Creates listener for the click event on provided DOM element,
         * which toggles associated with it collapsible model.
         */
        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
            var name = valueAccessor() || defaults.as,
                $collapsible = bindingCtx[name];

            if ($collapsible) {
                $(element).on('click', $collapsible.toggle);
            }
        }
    };

    renderer
        .addAttribute('collapsible')
        .addAttribute('openCollapsible')
        .addAttribute('closeCollapsible')
        .addAttribute('toggleCollapsible');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/staticChecked',[
    'ko',
    '../template/renderer'
], function (ko, renderer) {
    'use strict';

    ko.bindingHandlers.staticChecked = {
        'after': ['value', 'attr'],

        /**
         * Implements same functionality as a standard 'checked' binding,
         * but with a difference that it wont' change values array if
         * value of DOM element changes.
         */
        init: function (element, valueAccessor, allBindings) {
            var isCheckbox = element.type === 'checkbox',
                isRadio = element.type === 'radio',
                isValueArray,
                oldElemValue,
                useCheckedValue,
                checkedValue,
                updateModel,
                updateView;

            if (!isCheckbox && !isRadio) {
                return;
            }

            checkedValue = ko.pureComputed(function () {
                if (allBindings.has('checkedValue')) {
                    return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
                } else if (allBindings.has('value')) {
                    return ko.utils.unwrapObservable(allBindings.get('value'));
                }

                return element.value;
            });

            isValueArray = isCheckbox && ko.utils.unwrapObservable(valueAccessor()) instanceof Array;
            oldElemValue = isValueArray ? checkedValue() : undefined;
            useCheckedValue = isRadio || isValueArray;

            /**
             * Updates values array if it's necessary.
             */
            updateModel = function () {
                var isChecked = element.checked,
                    elemValue = useCheckedValue ? checkedValue() : isChecked,
                    modelValue;

                if (ko.computedContext.isInitial()) {
                    return;
                }

                if (isRadio && !isChecked) {
                    return;
                }

                modelValue = ko.dependencyDetection.ignore(valueAccessor);

                if (isValueArray) {
                    if (oldElemValue !== elemValue) {
                        oldElemValue = elemValue;
                    } else {
                        ko.utils.addOrRemoveItem(modelValue, elemValue, isChecked);
                    }
                } else {
                    ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
                }
            };

            /**
             * Updates checkbox state.
             */
            updateView = function () {
                var modelValue = ko.utils.unwrapObservable(valueAccessor());

                if (isValueArray) {
                    element.checked = ko.utils.arrayIndexOf(modelValue, checkedValue()) >= 0;
                } else if (isCheckbox) {
                    element.checked = modelValue;
                } else {
                    element.checked = checkedValue() === modelValue;
                }
            };

            ko.computed(updateModel, null, {
                disposeWhenNodeIsRemoved: element
            });

            ko.utils.registerEventHandler(element, 'click', updateModel);

            ko.computed(updateView, null, {
                disposeWhenNodeIsRemoved: element
            });
        }
    };

    ko.expressionRewriting._twoWayBindings.staticChecked = true;

    renderer.addAttribute('staticChecked');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/simple-checked',[
    'ko',
    '../template/renderer'
], function (ko, renderer) {
    'use strict';

    ko.bindingHandlers.simpleChecked = {
        'after': ['attr'],

        /**
         * Implements same functionality as a standard 'simpleChecked' binding,
         * but with a difference that it wont' change values array if
         * value of DOM element changes.
         */
        init: function (element, valueAccessor) {
            var isCheckbox = element.type === 'checkbox',
                isRadio = element.type === 'radio',
                updateView,
                updateModel;

            if (!isCheckbox && !isRadio) {
                return;
            }

            /**
             * Updates checked observable
             */
            updateModel = function () {
                var  modelValue = ko.dependencyDetection.ignore(valueAccessor),
                    isChecked = element.checked;

                if (ko.computedContext.isInitial()) {
                    return;
                }

                if (modelValue.peek() === isChecked) {
                    return;
                }

                if (isRadio && !isChecked) {
                    return;
                }

                modelValue(isChecked);
            };

            /**
             * Updates checkbox state
             */
            updateView = function () {
                var modelValue = ko.utils.unwrapObservable(valueAccessor());

                element.checked = !!modelValue;
            };

            ko.utils.registerEventHandler(element, 'change', updateModel);

            ko.computed(updateModel, null, {
                disposeWhenNodeIsRemoved: element
            });
            ko.computed(updateView, null, {
                disposeWhenNodeIsRemoved: element
            });
        }
    };

    ko.expressionRewriting._twoWayBindings.simpleChecked = true;

    renderer.addAttribute('simpleChecked');
    renderer.addAttribute('simple-checked', {
        binding: 'simpleChecked'
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/bind-html',[
    'ko',
    'underscore',
    'mage/apply/main',
    '../template/renderer'
], function (ko, _, mage, renderer) {
    'use strict';

    /**
     * Set html to node element.
     *
     * @param {HTMLElement} el - Element to apply bindings to.
     * @param {Function} html - Observable html content.
     */
    function setHtml(el, html) {
        ko.utils.emptyDomNode(el);
        html = ko.utils.unwrapObservable(html);

        if (!_.isNull(html) && !_.isUndefined(html)) {
            if (!_.isString(html)) {
                html = html.toString();
            }

            el.innerHTML = html;
        }
    }

    /**
     * Apply bindings and call magento attributes parser.
     *
     * @param {HTMLElement} el - Element to apply bindings to.
     * @param {ko.bindingContext} ctx - Instance of ko.bindingContext, passed to binding initially.
     */
    function applyComponents(el, ctx) {
        ko.utils.arrayForEach(el.childNodes, ko.cleanNode);
        ko.applyBindingsToDescendants(ctx, el);
        mage.apply();
    }

    ko.bindingHandlers.bindHtml = {
        /**
         * Scope binding's init method.
         *
         * @returns {Object} - Knockout declaration for it to let binding control descendants.
         */
        init: function () {
            return {
                controlsDescendantBindings: true
            };
        },

        /**
         * Reads params passed to binding.
         * Set html to node element, apply bindings and call magento attributes parser.
         *
         * @param {HTMLElement} el - Element to apply bindings to.
         * @param {Function} valueAccessor - Function that returns value, passed to binding.
         * @param {Object} allBindings - Object, which represents all bindings applied to element.
         * @param {Object} viewModel - Object, which represents view model binded to el.
         * @param {ko.bindingContext} bindingContext - Instance of ko.bindingContext, passed to binding initially.
         */
        update: function (el, valueAccessor, allBindings, viewModel, bindingContext) {
            setHtml(el, valueAccessor());
            applyComponents(el, bindingContext);
        }
    };

    renderer.addAttribute('bindHtml');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/bindings/tooltip',[
    'jquery',
    'ko',
    'underscore',
    'mage/template',
    'text!ui/template/tooltip/tooltip.html',
    '../template/renderer'
], function ($, ko, _, template, tooltipTmpl, renderer) {
    'use strict';

    var tooltip,
        defaults,
        positions,
        transformProp,
        checkedPositions = {},
        iterator = 0,
        previousTooltip,
        tooltipData,
        positionData = {},
        tooltipsCollection = {},
        isTouchDevice = (function () {
            return 'ontouchstart' in document.documentElement;
        })(),
        CLICK_EVENT = (function () {
            return isTouchDevice ? 'touchstart' : 'click';
        })();

    defaults = {
        tooltipWrapper: '[data-tooltip=tooltip-wrapper]',
        tooltipContentBlock: 'data-tooltip-content',
        closeButtonClass: 'action-close',
        tailClass: 'data-tooltip-tail',
        action: 'hover',
        delay: 300,
        track: false,
        step: 20,
        position: 'top',
        closeButton: false,
        showed: false,
        strict: true,
        center: false,
        closeOnScroll: true
    };

    tooltipData = {
        tooltipClasses: '',
        trigger: false,
        timeout: 0,
        element: false,
        event: false,
        targetElement: {},
        showed: false,
        currentID: 0
    };

    /**
     * Polyfill for css transform
     */
    transformProp = (function () {
        var style = document.createElement('div').style,
            base = 'Transform',
            vendors = ['webkit', 'moz', 'ms', 'o'],
            vi = vendors.length,
            property;

        if (typeof style.transform !== 'undefined') {
            return 'transform';
        }

        while (vi--) {
            property = vendors[vi] + base;

            if (typeof style[property] !== 'undefined') {
                return property;
            }
        }
    })();

    positions = {

        /*eslint max-depth: [0, 0]*/

        map: {
            horizontal: {
                s: 'w',
                p: 'left'
            },
            vertical: {
                s: 'h',
                p: 'top'
            }
        },

        /**
         * Wrapper function to get tooltip data (position, className, etc)
         *
         * @param {Object} s - object with sizes and positions elements
         * @returns {Object} tooltip data (position, className, etc)
         */
        top: function (s) {
            return positions._topLeftChecker(s, positions.map, 'vertical', '_bottom', 'top', 'right');
        },

        /**
         * Wrapper function to get tooltip data (position, className, etc)
         *
         * @param {Object} s - object with sizes and positions elements
         * @returns {Object} tooltip data (position, className, etc)
         */
        left: function (s) {
            return positions._topLeftChecker(s, positions.map, 'horizontal', '_right', 'left', 'top');
        },

        /**
         * Wrapper function to get tooltip data (position, className, etc)
         *
         * @param {Object} s - object with sizes and positions elements
         * @returns {Object} tooltip data (position, className, etc)
         */
        bottom: function (s) {
            return positions._bottomRightChecker(s, positions.map, 'vertical', '_top', 'bottom', 'left');
        },

        /**
         * Wrapper function to get tooltip data (position, className, etc)
         *
         * @param {Object} s - object with sizes and positions elements
         * @returns {Object} tooltip data (position, className, etc)
         */
        right: function (s) {
            return positions._bottomRightChecker(s, positions.map, 'horizontal', '_left', 'right', 'bottom');
        },

        /**
         * Check can tooltip setted on current position or not. If can't setted - delegate call.
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} map - mapping for get direction positions
         * @param {String} direction - vertical or horizontal
         * @param {String} className - class whats should be setted to tooltip
         * @param {String} side - parent method name
         * @param {String} delegate - method name if tooltip can't be setted in current position
         * @returns {Object} tooltip data (position, className, etc)
         */
        _topLeftChecker: function (s, map, direction, className, side, delegate) {
            var result = {
                    position: {}
                },
                config = tooltip.getTooltip(tooltipData.currentID),
                startPosition = !config.strict ? s.eventPosition : s.elementPosition,
                changedDirection;

            checkedPositions[side] = true;

            if (
                startPosition[map[direction].p] - s.tooltipSize[map[direction].s] - config.step >
                s.scrollPosition[map[direction].p]
            ) {
                result.position[map[direction].p] = startPosition[map[direction].p] - s.tooltipSize[map[direction].s] -
                    config.step;
                result.className = className;
                result.side = side;
                changedDirection = direction === 'vertical' ? 'horizontal' : 'vertical';
                result = positions._normalize(s, result, config, delegate, map, changedDirection);
            } else if (!checkedPositions[delegate]) {
                result = positions[delegate].apply(null, arguments);
            } else {
                result = positions.positionCenter(s, result);
            }

            return result;
        },

        /**
         * Check can tooltip setted on current position or not. If can't setted - delegate call.
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} map - mapping for get direction positions
         * @param {String} direction - vertical or horizontal
         * @param {String} className - class whats should be setted to tooltip
         * @param {String} side - parent method name
         * @param {String} delegate - method name if tooltip can't be setted in current position
         * @returns {Object} tooltip data (position, className, etc)
         */
        _bottomRightChecker: function (s, map, direction, className, side, delegate) {
            var result = {
                    position: {}
                },
                config = tooltip.getTooltip(tooltipData.currentID),
                startPosition = !config.strict ? s.eventPosition : {
                    top: s.elementPosition.top + s.elementSize.h,
                    left: s.elementPosition.left + s.elementSize.w
                },
                changedDirection;

            checkedPositions[side] = true;

            if (
                startPosition[map[direction].p] + s.tooltipSize[map[direction].s] + config.step <
                s.scrollPosition[map[direction].p] + s.windowSize[map[direction].s]
            ) {
                result.position[map[direction].p] = startPosition[map[direction].p] + config.step;
                result.className = className;
                result.side = side;
                changedDirection = direction === 'vertical' ? 'horizontal' : 'vertical';
                result = positions._normalize(s, result, config, delegate, map, changedDirection);
            } else if (!checkedPositions[delegate]) {
                result = positions[delegate].apply(null, arguments);
            } else {
                result = positions.positionCenter(s, result);
            }

            return result;
        },

        /**
         * Centered tooltip if tooltip does not fit in window
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} data - current data (position, className, etc)
         * @returns {Object} tooltip data (position, className, etc)
         */
        positionCenter: function (s, data) {
            data = positions._positionCenter(s, data, 'horizontal', positions.map);
            data = positions._positionCenter(s, data, 'vertical', positions.map);

            return data;
        },

        /**
         * Centered tooltip side
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} data - current data (position, className, etc)
         * @param {String} direction - vertical or horizontal
         * @param {Object} map - mapping for get direction positions
         * @returns {Object} tooltip data (position, className, etc)
         */
        _positionCenter: function (s, data, direction, map) {
            if (s.tooltipSize[map[direction].s] < s.windowSize[map[direction].s]) {
                data.position[map[direction].p] = (s.windowSize[map[direction].s] -
                    s.tooltipSize[map[direction].s]) / 2 + s.scrollPosition[map[direction].p];
            } else {
                data.position[map[direction].p] = s.scrollPosition[map[direction].p];
                data.tooltipSize = {};
                data.tooltipSize[map[direction].s] = s.windowSize[map[direction].s];
            }

            return data;
        },

        /**
         * Normalize horizontal or vertical position.
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} data - current data (position, className, etc)
         * @param {Object} config - tooltip config
         * @param {String} delegate - method name if tooltip can't be setted in current position
         * @param {Object} map - mapping for get direction positions
         * @param {String} direction - vertical or horizontal
         * @returns {Object} tooltip data (position, className, etc)
         */
        _normalize: function (s, data, config, delegate, map, direction) {
            var startPosition = !config.center ? s.eventPosition : {
                    left: s.elementPosition.left + s.elementSize.w / 2,
                    top: s.elementPosition.top + s.elementSize.h / 2
                },
                depResult;

            if (startPosition[map[direction].p] - s.tooltipSize[map[direction].s] / 2 >
                s.scrollPosition[map[direction].p] && startPosition[map[direction].p] +
                s.tooltipSize[map[direction].s] / 2 <
                s.scrollPosition[map[direction].p] + s.windowSize[map[direction].s]
            ) {
                data.position[map[direction].p] = startPosition[map[direction].p] - s.tooltipSize[map[direction].s] / 2;
            } else {

                /*eslint-disable no-lonely-if*/
                if (!checkedPositions[delegate]) {
                    depResult = positions[delegate].apply(null, arguments);

                    if (depResult.hasOwnProperty('className')) {
                        data = depResult;
                    } else {
                        data = positions._normalizeTail(s, data, config, delegate, map, direction, startPosition);
                    }
                } else {
                    data = positions._normalizeTail(s, data, config, delegate, map, direction, startPosition);
                }
            }

            return data;
        },

        /**
         * Calc tail position.
         *
         * @param {Object} s - object with sizes and positions elements
         * @param {Object} data - current data (position, className, etc)
         * @param {Object} config - tooltip config
         * @param {String} delegate - method name if tooltip can't be setted in current position
         * @param {Object} map - mapping for get direction positions
         * @param {String} direction - vertical or horizontal
         * @param {Object} startPosition - start position
         * @returns {Object} tooltip data (position, className, etc)
         */
        _normalizeTail: function (s, data, config, delegate, map, direction, startPosition) {
            data.tail = {};

            if (s.tooltipSize[map[direction].s] < s.windowSize[map[direction].s]) {

                if (
                    startPosition[map[direction].p] >
                    s.windowSize[map[direction].s] / 2 + s.scrollPosition[map[direction].p]
                ) {
                    data.position[map[direction].p] = s.windowSize[map[direction].s] +
                        s.scrollPosition[map[direction].p] - s.tooltipSize[map[direction].s];
                    data.tail[map[direction].p] = startPosition[map[direction].p] -
                        s.tooltipSize[map[direction].s] / 2 - data.position[map[direction].p];
                } else {
                    data.position[map[direction].p] = s.scrollPosition[map[direction].p];
                    data.tail[map[direction].p] = startPosition[map[direction].p] -
                        s.tooltipSize[map[direction].s] / 2 - data.position[map[direction].p];
                }
            } else {
                data.position[map[direction].p] = s.scrollPosition[map[direction].p];
                data.tail[map[direction].p] = s.eventPosition[map[direction].p] - s.windowSize[map[direction].s] / 2;
                data.tooltipSize = {};
                data.tooltipSize[map[direction].s] = s.windowSize[map[direction].s];
            }

            return data;
        }
    };

    tooltip = {

        /**
         * Set new tooltip to tooltipCollection, save config, and add unic id
         *
         * @param {Object} config - tooltip config
         * @returns {String} tooltip id
         */
        setTooltip: function (config) {
            var property = 'id-' + iterator;

            tooltipsCollection[property] = config;
            iterator++;

            return property;
        },

        /**
         * Get tooltip config by id
         *
         * @param {String} id - tooltip id
         * @returns {Object} tooltip config
         */
        getTooltip: function (id) {
            return tooltipsCollection[id];
        },

        /**
         * Set content to current tooltip
         *
         * @param {Object} tooltipElement - tooltip element
         * @param {Object} viewModel - tooltip view model
         * @param {String} id - tooltip id
         * @param {Object} bindingCtx - tooltip context
         * @param {Object} event - action event
         */
        setContent: function (tooltipElement, viewModel, id, bindingCtx, event) {
            var html = $(tooltipElement).html(),
                config = tooltip.getTooltip(id),
                body = $('body');

            tooltipData.currentID = id;
            tooltipData.trigger = $(event.currentTarget);
            tooltip.setTargetData(event);
            body.on('mousemove.setTargetData', tooltip.setTargetData);
            tooltip.clearTimeout(id);

            tooltipData.timeout = _.delay(function () {
                body.off('mousemove.setTargetData', tooltip.setTargetData);

                if (tooltipData.trigger[0] === tooltipData.targetElement) {
                    tooltip.destroy(id);
                    event.stopPropagation();
                    tooltipElement = tooltip.createTooltip(id);
                    tooltipElement.find('.' + defaults.tooltipContentBlock).append(html);
                    tooltipElement.applyBindings(bindingCtx);
                    tooltip.setHandlers(id);
                    tooltip.setPosition(tooltipElement, id);
                    previousTooltip = id;
                }

            }, config.delay);
        },

        /**
         * Set position to current tooltip
         *
         * @param {Object} tooltipElement - tooltip element
         * @param {String} id - tooltip id
         */
        setPosition: function (tooltipElement, id) {
            var config = tooltip.getTooltip(id);

            tooltip.sizeData = {
                windowSize: {
                    h: $(window).outerHeight(),
                    w: $(window).outerWidth()
                },
                scrollPosition: {
                    top: $(window).scrollTop(),
                    left: $(window).scrollLeft()
                },
                tooltipSize: {
                    h: tooltipElement.outerHeight(),
                    w: tooltipElement.outerWidth()
                },
                elementSize: {
                    h: tooltipData.trigger.outerHeight(),
                    w: tooltipData.trigger.outerWidth()
                },
                elementPosition: tooltipData.trigger.offset(),
                eventPosition: this.getEventPosition(tooltipData.event)
            };

            _.extend(positionData, positions[config.position](tooltip.sizeData));
            tooltipElement.css(positionData.position);
            tooltipElement.addClass(positionData.className);
            tooltip._setTooltipSize(positionData, tooltipElement);
            tooltip._setTailPosition(positionData, tooltipElement);
            checkedPositions = {};
        },

        /**
         * Check position data and change tooltip size if needs
         *
         * @param {Object} data - position data
         * @param {Object} tooltipElement - tooltip element
         */
        _setTooltipSize: function (data, tooltipElement) {
            if (data.tooltipSize) {
                data.tooltipSize.w ?
                    tooltipElement.css('width', data.tooltipSize.w) :
                    tooltipElement.css('height', data.tooltipSize.h);
            }
        },

        /**
         * Check position data and set position to tail
         *
         * @param {Object} data - position data
         * @param {Object} tooltipElement - tooltip element
         */
        _setTailPosition: function (data, tooltipElement) {
            var tail,
                tailMargin;

            if (data.tail) {
                tail = tooltipElement.find('.' + defaults.tailClass);

                if (data.tail.left) {
                    tailMargin = parseInt(tail.css('margin-left'), 10);
                    tail.css('margin-left', tailMargin + data.tail.left);
                } else {
                    tailMargin = parseInt(tail.css('margin-top'), 10);
                    tail.css('margin-top', tailMargin + data.tail.top);
                }
            }
        },

        /**
         * Resolves position for tooltip
         *
         * @param {Object} event
         * @returns {Object}
         */
        getEventPosition: function (event) {
            var position = {
                left: event.originalEvent && event.originalEvent.pageX || 0,
                top: event.originalEvent && event.originalEvent.pageY || 0
            };

            if (position.left === 0 && position.top === 0) {
                _.extend(position, event.target.getBoundingClientRect());
            }

            return position;
        },

        /**
         * Close tooltip if action happened outside handler and tooltip element
         *
         * @param {String} id - tooltip id
         * @param {Object} event - action event
         */
        outerClick: function (id, event) {
            var tooltipElement = $(event.target).parents(defaults.tooltipWrapper)[0],
                isTrigger = event.target === tooltipData.trigger[0] || $.contains(tooltipData.trigger[0], event.target);

            if (tooltipData.showed && tooltipElement !== tooltipData.element[0] && !isTrigger) {
                tooltip.destroy(id);
            }
        },

        /**
         * Parse keydown event and if event trigger is escape key - close tooltip
         *
         * @param {Object} event - action event
         */
        keydownHandler: function (event) {
            if (tooltipData.showed && event.keyCode === 27) {
                tooltip.destroy(tooltipData.currentID);
            }
        },

        /**
         * Change tooltip position when track is enabled
         *
         * @param {Object} event - current event
         */
        track: function (event) {
            var inequality = {},
                map = positions.map,
                translate = {
                    left: 'translateX',
                    top: 'translateY'
                },
                eventPosition = {
                    left: event.pageX,
                    top: event.pageY
                },
                tooltipSize = {
                    w: tooltipData.element.outerWidth(),
                    h: tooltipData.element.outerHeight()
                },
                direction = positionData.side === 'bottom' || positionData.side === 'top' ? 'horizontal' : 'vertical';

            inequality[map[direction].p] = eventPosition[map[direction].p] - (positionData.position[map[direction].p] +
                tooltipSize[map[direction].s] / 2);

            if (positionData.position[map[direction].p] + inequality[map[direction].p] +
                tooltip.sizeData.tooltipSize[map[direction].s] >
                tooltip.sizeData.windowSize[map[direction].s] + tooltip.sizeData.scrollPosition[map[direction].p] ||
                inequality[map[direction].p] + positionData.position[map[direction].p] <
                tooltip.sizeData.scrollPosition[map[direction].p]) {

                return false;
            }

            tooltipData.element[0].style[transformProp] = translate[map[direction].p] +
                '(' + inequality[map[direction].p] + 'px)';
        },

        /**
         * Set handlers to tooltip
         *
         * @param {String} id - tooltip id
         */
        setHandlers: function (id) {
            var config = tooltip.getTooltip(id);

            if (config.track) {
                tooltipData.trigger.on('mousemove.track', tooltip.track);
            }

            if (config.action === 'click') {
                $(window).on(CLICK_EVENT + '.outerClick', tooltip.outerClick.bind(null, id));
            }

            if (config.closeButton) {
                $('.' + config.closeButtonClass).on('click.closeButton', tooltip.destroy.bind(null, id));
            }

            if (config.closeOnScroll) {
                document.addEventListener('scroll', tooltip.destroy, true);
                $(window).on('scroll.tooltip', tooltip.outerClick.bind(null, id));
            }

            $(window).on('keydown.tooltip', tooltip.keydownHandler);
            $(window).on('resize.outerClick', tooltip.outerClick.bind(null, id));
        },

        /**
         * Toggle tooltip
         *
         * @param {Object} tooltipElement - tooltip element
         * @param {Object} viewModel - tooltip view model
         * @param {String} id - tooltip id
         */
        toggleTooltip: function (tooltipElement, viewModel, id) {
            if (previousTooltip === id && tooltipData.showed) {
                tooltip.destroy(id);

                return false;
            }

            tooltip.setContent.apply(null, arguments);

            return false;
        },

        /**
         * Create tooltip and append to DOM
         *
         * @param {String} id - tooltip id
         * @returns {Object} tooltip element
         */
        createTooltip: function (id) {
            var body = $('body'),
                config = tooltip.getTooltip(id);

            $(template(tooltipTmpl, {
                data: config
            })).appendTo(body);

            tooltipData.showed = true;
            tooltipData.element = $(config.tooltipWrapper);

            return tooltipData.element;
        },

        /**
         * Check action and clean timeout
         *
         * @param {String} id - tooltip id
         */
        clearTimeout: function (id) {
            var config = tooltip.getTooltip(id);

            if (config.action === 'hover') {
                clearTimeout(tooltipData.timeout);
            }
        },

        /**
         * Check previous tooltip
         */
        checkPreviousTooltip: function () {
            if (!tooltipData.timeout) {
                tooltip.destroy();
            }
        },

        /**
         * Destroy tooltip instance
         */
        destroy: function () {
            if (tooltipData.element) {
                tooltipData.element.remove();
                tooltipData.showed = false;
            }

            positionData = {};
            tooltipData.timeout = false;
            tooltip.removeHandlers();
        },

        /**
         * Remove tooltip handlers
         */
        removeHandlers: function () {
            $('.' + defaults.closeButtonClass).off('click.closeButton');
            tooltipData.trigger.off('mousemove.track');
            document.removeEventListener('scroll', tooltip.destroy, true);
            $(window).off('scroll.tooltip');
            $(window).off(CLICK_EVENT + '.outerClick');
            $(window).off('keydown.tooltip');
            $(window).off('resize.outerClick');
        },

        /**
         * Set target element
         *
         * @param {Object} event - current event
         */
        setTargetData: function (event) {
            tooltipData.event = event;

            //TODO: bug chrome v.49; Link to issue https://bugs.chromium.org/p/chromium/issues/detail?id=161464
            if (event.timeStamp - (tooltipData.timestamp || 0) < 1) {
                return;
            }

            if (event.type === 'mousemove') {
                tooltipData.targetElement = event.target;
            } else {
                tooltipData.targetElement = event.currentTarget;
                tooltipData.timestamp = event.timeStamp;
            }
        },

        /**
         * Merged user config with defaults configuration
         *
         * @param {Object} config - user config
         * @returns {Object} merged config
         */
        processingConfig: function (config) {
            return _.extend({}, defaults, config);
        }
    };

    ko.bindingHandlers.tooltip = {

        /**
         * Initialize tooltip
         *
         * @param {Object} elem - tooltip DOM element
         * @param {Function} valueAccessor - ko observable property, tooltip data
         * @param {Object} allBindings - all bindings on current element
         * @param {Object} viewModel - current element viewModel
         * @param {Object} bindingCtx - current element binding context
         */
        init: function (elem, valueAccessor, allBindings, viewModel, bindingCtx) {
            var config = tooltip.processingConfig(valueAccessor()),
                $parentScope = config.parentScope ? $(config.parentScope) : $(elem).parent(),
                tooltipId;

            $(elem).addClass('hidden');

            if (isTouchDevice) {
                config.action = 'click';
            }
            tooltipId = tooltip.setTooltip(config);

            if (config.action === 'hover') {
                $parentScope.on(
                    'mouseenter',
                    config.trigger,
                    tooltip.setContent.bind(null, elem, viewModel, tooltipId, bindingCtx)
                );
                $parentScope.on(
                    'mouseleave',
                    config.trigger,
                    tooltip.checkPreviousTooltip.bind(null, tooltipId)
                );
            } else if (config.action === 'click') {
                $parentScope.on(
                    'click',
                    config.trigger,
                    tooltip.toggleTooltip.bind(null, elem, viewModel, tooltipId, bindingCtx)
                );
            }

            return {
                controlsDescendantBindings: true
            };
        }
    };

    renderer.addAttribute('tooltip');
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/knockout/extender/observable_array',[
    'ko',
    'underscore'
], function (ko, _) {
    'use strict';

    /**
     * Iterator function.
     *
     * @param {String} callback
     * @param {Array} args
     * @param {Object} elem
     * @returns {*}
     */
    function iterator(callback, args, elem) {
        callback = elem[callback];

        if (_.isFunction(callback)) {
            return callback.apply(elem, args);
        }

        return callback;
    }

    /**
     * Wrapper function.
     *
     * @param {String} method
     * @returns {Function}
     */
    function wrapper(method) {
        return function (iteratee) {
            var callback = iteratee,
                elems = this(),
                args = _.toArray(arguments);

            if (_.isString(iteratee)) {
                callback = iterator.bind(null, iteratee, args.slice(1));

                args.unshift(callback);
            }

            args.unshift(elems);

            return _[method].apply(_, args);
        };
    }

    _.extend(ko.observableArray.fn, {
        each: wrapper('each'),

        map: wrapper('map'),

        filter: wrapper('filter'),

        some: wrapper('some'),

        every: wrapper('every'),

        groupBy: wrapper('groupBy'),

        sortBy: wrapper('sortBy'),

        /**
         * Wrapper for underscore findWhere function.
         *
         * @param {Object} properties
         * @return {Object}
         */
        findWhere: function (properties) {
            return _.findWhere(this(), properties);
        },

        /**
         * Wrapper for underscore contains function.
         *
         * @param {*} value
         * @return {Boolean}
         */
        contains: function (value) {
            return _.contains(this(), value);
        },

        /**
         * Inverse contains call.
         *
         * @return {Boolean}
         */
        hasNo: function () {
            return !this.contains.apply(this, arguments);
        },

        /**
         * Getter for length property.
         *
         * @return {Number}
         */
        getLength: function () {
            return this().length;
        },

        /**
         * Create object with keys that gets from each object property.
         *
         * @return {Object}
         */
        indexBy: function (key) {
            return _.indexBy(this(), key);
        },

        /**
         * Returns a copy of the array with all instances of the values removed.
         *
         * @return {Array}
         */
        without: function () {
            var args = Array.prototype.slice.call(arguments);

            args.unshift(this());

            return _.without.apply(_, args);
        },

        /**
         * Returns the first element of an array.
         *
         * @return {*}
         */
        first: function () {
            return _.first(this());
        },

        /**
         * Returns the last element of an array
         *
         * @return {*}
         */
        last: function () {
            return _.last(this());
        },

        /**
         * Iterate and pick provided properties.
         *
         * @return {Array}
         */
        pluck: function () {
            var args = Array.prototype.slice.call(arguments);

            args.unshift(this());

            return _.pluck.apply(_, args);
        }
    });
});

// REPEAT binding for Knockout http://knockoutjs.com/
// (c) Michael Best
// License: MIT (http://www.opensource.org/licenses/mit-license.php)
// Version 2.1.0

(function(factory) {
    if (typeof define === 'function' && define.amd) {
        // [1] AMD anonymous module
        define('knockoutjs/knockout-repeat',['knockout'], factory);
    } else if (typeof exports === 'object') {
        // [2] commonJS
        factory(require('knockout'));
    } else {
        // [3] No module loader (plain <script> tag) - put directly in global namespace
        factory(window.ko);
    }
})(function(ko) {

if (!ko.virtualElements)
    throw Error('Repeat requires at least Knockout 2.1');

var ko_bindingFlags = ko.bindingFlags || {};
var ko_unwrap = ko.utils.unwrapObservable;

var koProtoName = '__ko_proto__';

if (ko.version >= "3.0.0") {
    // In Knockout 3.0.0, use the node preprocessor to replace a node with a repeat binding with a virtual element
    var provider = ko.bindingProvider.instance, previousPreprocessFn = provider.preprocessNode;
    provider.preprocessNode = function(node) {
        var newNodes, nodeBinding;
        if (!previousPreprocessFn || !(newNodes = previousPreprocessFn.call(this, node))) {
            if (node.nodeType === 1 && (nodeBinding = node.getAttribute('data-bind'))) {
                if (/^\s*repeat\s*:/.test(nodeBinding)) {
                    var leadingComment = node.ownerDocument.createComment('ko ' + nodeBinding),
                        trailingComment = node.ownerDocument.createComment('/ko');
                    node.parentNode.insertBefore(leadingComment, node);
                    node.parentNode.insertBefore(trailingComment, node.nextSibling);
                    node.removeAttribute('data-bind');
                    newNodes = [leadingComment, node, trailingComment];
                }
            }
        }
        return newNodes;
    };
}

ko.virtualElements.allowedBindings.repeat = true;
ko.bindingHandlers.repeat = {
    flags: ko_bindingFlags.contentBind | ko_bindingFlags.canUseVirtual,
    init: function(element, valueAccessor, allBindingsAccessor, xxx, bindingContext) {

        // Read and set fixed options--these options cannot be changed
        var repeatParam = ko_unwrap(valueAccessor());
        if (repeatParam && typeof repeatParam == 'object' && !('length' in repeatParam)) {
            var repeatIndex = repeatParam.index,
                repeatData = repeatParam.item,
                repeatStep = repeatParam.step,
                repeatReversed = repeatParam.reverse,
                repeatBind = repeatParam.bind,
                repeatInit = repeatParam.init,
                repeatUpdate = repeatParam.update;
        }
        // Set default values for options that need it
        repeatIndex = repeatIndex || '$index';
        repeatData = repeatData || ko.bindingHandlers.repeat.itemName || '$item';
        repeatStep = repeatStep || 1;
        repeatReversed = repeatReversed || false;

        var parent = element.parentNode, placeholder;
        if (element.nodeType == 8) {    // virtual element
            // Extract the "children" and find the single element node
            var childNodes = ko.utils.arrayFilter(ko.virtualElements.childNodes(element), function(node) { return node.nodeType == 1;});
            if (childNodes.length !== 1) {
                throw Error("Repeat binding requires a single element to repeat");
            }
            ko.virtualElements.emptyNode(element);

            // The placeholder is the closing comment normally, or the opening comment if reversed
            placeholder = repeatReversed ? element : element.nextSibling;
            // The element to repeat is the contained element
            element = childNodes[0];
        } else {    // regular element
            // First clean the element node and remove node's binding
            var origBindString = element.getAttribute('data-bind');
            ko.cleanNode(element);
            element.removeAttribute('data-bind');

            // Original element is no longer needed: delete it and create a placeholder comment
            placeholder = element.ownerDocument.createComment('ko_repeatplaceholder ' + origBindString);
            parent.replaceChild(placeholder, element);
        }

        // extract and remove a data-repeat-bind attribute, if present
        if (!repeatBind) {
            repeatBind = element.getAttribute('data-repeat-bind');
            if (repeatBind) {
                element.removeAttribute('data-repeat-bind');
            }
        }

        // Make a copy of the element node to be copied for each repetition
        var cleanNode = element.cloneNode(true);
        if (typeof repeatBind == "string") {
            cleanNode.setAttribute('data-bind', repeatBind);
            repeatBind = null;
        }

        // Set up persistent data
        var lastRepeatCount = 0,
            notificationObservable = ko.observable(),
            repeatArray, arrayObservable;

        if (repeatInit) {
            repeatInit(parent);
        }

        var subscribable = ko.computed(function() {
            function makeArrayItemAccessor(index) {
                var f = function(newValue) {
                    var item = repeatArray[index];
                    // Reading the value of the item
                    if (!arguments.length) {
                        notificationObservable();   // for dependency tracking
                        return ko_unwrap(item);
                    }
                    // Writing a value to the item
                    if (ko.isObservable(item)) {
                        item(newValue);
                    } else if (arrayObservable && arrayObservable.splice) {
                        arrayObservable.splice(index, 1, newValue);
                    } else {
                        repeatArray[index] = newValue;
                    }
                    return this;
                };
                // Pretend that our accessor function is an observable
                f[koProtoName] = ko.observable;
                return f;
            }

            function makeBinding(item, index, context) {
                return repeatArray
                    ? function() { return repeatBind.call(bindingContext.$data, item, index, context); }
                    : function() { return repeatBind.call(bindingContext.$data, index, context); }
            }

            // Read and set up variable options--these options can change and will update the binding
            var paramObservable = valueAccessor(), repeatParam = ko_unwrap(paramObservable), repeatCount = 0;
            if (repeatParam && typeof repeatParam == 'object') {
                if ('length' in repeatParam) {
                    repeatArray = repeatParam;
                    repeatCount = repeatArray.length;
                } else {
                    if ('foreach' in repeatParam) {
                        repeatArray = ko_unwrap(paramObservable = repeatParam.foreach);
                        if (repeatArray && typeof repeatArray == 'object' && 'length' in repeatArray) {
                            repeatCount = repeatArray.length || 0;
                        } else {
                            repeatCount = repeatArray || 0;
                            repeatArray = null;
                        }
                    }
                    // If a count value is provided (>0), always output that number of items
                    if ('count' in repeatParam)
                        repeatCount = ko_unwrap(repeatParam.count) || repeatCount;
                    // If a limit is provided, don't output more than the limit
                    if ('limit' in repeatParam)
                        repeatCount = Math.min(repeatCount, ko_unwrap(repeatParam.limit)) || repeatCount;
                }
                arrayObservable = repeatArray && ko.isObservable(paramObservable) ? paramObservable : null;
            } else {
                repeatCount = repeatParam || 0;
            }

            // Remove nodes from end if array is shorter
            for (; lastRepeatCount > repeatCount; lastRepeatCount-=repeatStep) {
                ko.removeNode(repeatReversed ? placeholder.nextSibling : placeholder.previousSibling);
            }

            // Notify existing nodes of change
            notificationObservable.notifySubscribers();

            // Add nodes to end if array is longer (also initially populates nodes)
            for (; lastRepeatCount < repeatCount; lastRepeatCount+=repeatStep) {
                // Clone node and add to document
                var newNode = cleanNode.cloneNode(true);
                parent.insertBefore(newNode, repeatReversed ? placeholder.nextSibling : placeholder);
                newNode.setAttribute('data-repeat-index', lastRepeatCount);

                // Apply bindings to inserted node
                if (repeatArray && repeatData == '$data') {
                    var newContext = bindingContext.createChildContext(makeArrayItemAccessor(lastRepeatCount));
                } else {
                    var newContext = bindingContext.extend();
                    if (repeatArray)
                        newContext[repeatData] = makeArrayItemAccessor(lastRepeatCount);
                }
                newContext[repeatIndex] = lastRepeatCount;
                if (repeatBind) {
                    var result = ko.applyBindingsToNode(newNode, makeBinding(newContext[repeatData], lastRepeatCount, newContext), newContext, true),
                        shouldBindDescendants = result && result.shouldBindDescendants;
                }
                if (!repeatBind || (result && shouldBindDescendants !== false)) {
                    ko.applyBindings(newContext, newNode);
                }
            }
            if (repeatUpdate) {
                repeatUpdate(parent);
            }
        }, null, {disposeWhenNodeIsRemoved: placeholder});

        return { controlsDescendantBindings: true, subscribable: subscribable };
    }
};
});
/*!
  Knockout Fast Foreach v0.4.1 (2015-07-17T14:06:15.974Z)
  By: Brian M Hunt (C) 2015
  License: MIT

  Adds `fastForEach` to `ko.bindingHandlers`.
*/
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define('knockoutjs/knockout-fast-foreach',['knockout'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('knockout'));
  } else {
    root.KnockoutFastForeach = factory(root.ko);
  }
}(this, function (ko) {
  "use strict";
// index.js
// --------
// Fast For Each
//
// Employing sound techniques to make a faster Knockout foreach binding.
// --------

//      Utilities

// from https://github.com/jonschlinkert/is-plain-object
function isPlainObject(o) {
  return !!o && typeof o === 'object' && o.constructor === Object;
}

// From knockout/src/virtualElements.js
var commentNodesHaveTextProperty = document && document.createComment("test").text === "<!--test-->";
var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
var supportsDocumentFragment = document && typeof document.createDocumentFragment === "function";
function isVirtualNode(node) {
  return (node.nodeType === 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
}


// Get a copy of the (possibly virtual) child nodes of the given element,
// put them into a container, then empty the given node.
function makeTemplateNode(sourceNode) {
  var container = document.createElement("div");
  var parentNode;
  if (sourceNode.content) {
    // For e.g. <template> tags
    parentNode = sourceNode.content;
  } else if (sourceNode.tagName === 'SCRIPT') {
    parentNode = document.createElement("div");
    parentNode.innerHTML = sourceNode.text;
  } else {
    // Anything else e.g. <div>
    parentNode = sourceNode;
  }
  ko.utils.arrayForEach(ko.virtualElements.childNodes(parentNode), function (child) {
    // FIXME - This cloneNode could be expensive; we may prefer to iterate over the
    // parentNode children in reverse (so as not to foul the indexes as childNodes are
    // removed from parentNode when inserted into the container)
    if (child) {
      container.insertBefore(child.cloneNode(true), null);
    }
  });
  return container;
}

function insertAllAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode) {
  var frag, len, i;
  // poor man's node and array check, should be enough for this
  if (typeof nodeOrNodeArrayToInsert.nodeType !== "undefined" && typeof nodeOrNodeArrayToInsert.length === "undefined") {
    throw new Error("Expected a single node or a node array");
  }

  if (typeof nodeOrNodeArrayToInsert.nodeType !== "undefined") {
    ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode);
    return;
  }

  if (nodeOrNodeArrayToInsert.length === 1) {
    ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert[0], insertAfterNode);
    return;
  }

  if (supportsDocumentFragment) {
    frag = document.createDocumentFragment();

    for (i = 0, len = nodeOrNodeArrayToInsert.length; i !== len; ++i) {
      frag.appendChild(nodeOrNodeArrayToInsert[i]);
    }
    ko.virtualElements.insertAfter(containerNode, frag, insertAfterNode);
  } else {
    // Nodes are inserted in reverse order - pushed down immediately after
    // the last node for the previous item or as the first node of element.
    for (i = nodeOrNodeArrayToInsert.length - 1; i >= 0; --i) {
      var child = nodeOrNodeArrayToInsert[i];
      if (!child) {
        return;
      }
      ko.virtualElements.insertAfter(containerNode, child, insertAfterNode);
    }
  }
}

// Mimic a KO change item 'add'
function valueToChangeAddItem(value, index) {
  return {
    status: 'added',
    value: value,
    index: index
  };
}

function isAdditionAdjacentToLast(changeIndex, arrayChanges) {
  return changeIndex > 0 &&
    changeIndex < arrayChanges.length &&
    arrayChanges[changeIndex].status === "added" &&
    arrayChanges[changeIndex - 1].status === "added" &&
    arrayChanges[changeIndex - 1].index === arrayChanges[changeIndex].index - 1;
}

function FastForEach(spec) {
  this.element = spec.element;
  this.container = isVirtualNode(this.element) ?
                   this.element.parentNode : this.element;
  this.$context = spec.$context;
  this.data = spec.data;
  this.as = spec.as;
  this.noContext = spec.noContext;
  this.templateNode = makeTemplateNode(
    spec.name ? document.getElementById(spec.name).cloneNode(true) : spec.element
  );
  this.afterQueueFlush = spec.afterQueueFlush;
  this.beforeQueueFlush = spec.beforeQueueFlush;
  this.changeQueue = [];
  this.lastNodesList = [];
  this.indexesToDelete = [];
  this.rendering_queued = false;

  // Remove existing content.
  ko.virtualElements.emptyNode(this.element);

  // Prime content
  var primeData = ko.unwrap(this.data);
  if (primeData.map) {
    this.onArrayChange(primeData.map(valueToChangeAddItem));
  }

  // Watch for changes
  if (ko.isObservable(this.data)) {
    if (!this.data.indexOf) {
      // Make sure the observable is trackable.
      this.data = this.data.extend({trackArrayChanges: true});
    }
    this.changeSubs = this.data.subscribe(this.onArrayChange, this, 'arrayChange');
  }
}


FastForEach.animateFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame || window.msRequestAnimationFrame ||
  function(cb) { return window.setTimeout(cb, 1000 / 60); };


FastForEach.prototype.dispose = function () {
  if (this.changeSubs) {
    this.changeSubs.dispose();
  }
};


// If the array changes we register the change.
FastForEach.prototype.onArrayChange = function (changeSet) {
  var self = this;
  var changeMap = {
    added: [],
    deleted: []
  };
  for (var i = 0, len = changeSet.length; i < len; i++) {
    // the change is appended to a last change info object when both are 'added' and have indexes next to each other
    // here I presume that ko is sending changes in monotonic order (in index variable) which happens to be true, tested with push and splice with multiple pushed values
    if (isAdditionAdjacentToLast(i, changeSet)) {
      var batchValues = changeMap.added[changeMap.added.length - 1].values;
      if (!batchValues) {
        // transform the last addition into a batch addition object
        var lastAddition = changeMap.added.pop();
        var batchAddition = {
          isBatch: true,
          status: 'added',
          index: lastAddition.index,
          values: [lastAddition.value]
        };
        batchValues = batchAddition.values;
        changeMap.added.push(batchAddition);
      }
      batchValues.push(changeSet[i].value);
    } else {
      changeMap[changeSet[i].status].push(changeSet[i]);
    }
  }
  if (changeMap.deleted.length > 0) {
    this.changeQueue.push.apply(this.changeQueue, changeMap.deleted);
    this.changeQueue.push({status: 'clearDeletedIndexes'});
  }
  this.changeQueue.push.apply(this.changeQueue, changeMap.added);
  // Once a change is registered, the ticking count-down starts for the processQueue.
  if (this.changeQueue.length > 0 && !this.rendering_queued) {
    this.rendering_queued = true;
    FastForEach.animateFrame.call(window, function () { self.processQueue(); });
  }
};


// Reflect all the changes in the queue in the DOM, then wipe the queue.
FastForEach.prototype.processQueue = function () {
  var self = this;

  // Callback so folks can do things before the queue flush.
  if (typeof this.beforeQueueFlush === 'function') {
    this.beforeQueueFlush(this.changeQueue);
  }

  ko.utils.arrayForEach(this.changeQueue, function (changeItem) {
    // console.log(self.data(), "CI", JSON.stringify(changeItem, null, 2), JSON.stringify($(self.element).text()))
    self[changeItem.status](changeItem);
    // console.log("  ==> ", JSON.stringify($(self.element).text()))
  });
  this.rendering_queued = false;
  // Callback so folks can do things.
  if (typeof this.afterQueueFlush === 'function') {
    this.afterQueueFlush(this.changeQueue);
  }
  this.changeQueue = [];
};


// Process a changeItem with {status: 'added', ...}
FastForEach.prototype.added = function (changeItem) {
  var index = changeItem.index;
  var valuesToAdd = changeItem.isBatch ? changeItem.values : [changeItem.value];
  var referenceElement = this.lastNodesList[index - 1] || null;
  // gather all childnodes for a possible batch insertion
  var allChildNodes = [];

  for (var i = 0, len = valuesToAdd.length; i < len; ++i) {
    var templateClone = this.templateNode.cloneNode(true);
    var childContext;

    if (this.noContext) {
      childContext = this.$context.extend({
        '$item': valuesToAdd[i]
      });
    } else {
      childContext = this.$context.createChildContext(valuesToAdd[i], this.as || null);
    }

    // apply bindings first, and then process child nodes, because bindings can add childnodes
    ko.applyBindingsToDescendants(childContext, templateClone);

    var childNodes = ko.virtualElements.childNodes(templateClone);
    // Note discussion at https://github.com/angular/angular.js/issues/7851
    allChildNodes.push.apply(allChildNodes, Array.prototype.slice.call(childNodes));
    this.lastNodesList.splice(index + i, 0, childNodes[childNodes.length - 1]);
  }

  insertAllAfter(this.element, allChildNodes, referenceElement);
};


// Process a changeItem with {status: 'deleted', ...}
FastForEach.prototype.deleted = function (changeItem) {
  var index = changeItem.index;
  var ptr = this.lastNodesList[index],
      // We use this.element because that will be the last previous node
      // for virtual element lists.
      lastNode = this.lastNodesList[index - 1] || this.element;
  do {
    ptr = ptr.previousSibling;
    ko.removeNode((ptr && ptr.nextSibling) || ko.virtualElements.firstChild(this.element));
  } while (ptr && ptr !== lastNode);
  // The "last node" in the DOM from which we begin our delets of the next adjacent node is
  // now the sibling that preceded the first node of this item.
  this.lastNodesList[index] = this.lastNodesList[index - 1];
  this.indexesToDelete.push(index);
};


// We batch our deletion of item indexes in our parallel array.
// See brianmhunt/knockout-fast-foreach#6/#8
FastForEach.prototype.clearDeletedIndexes = function () {
  // We iterate in reverse on the presumption (following the unit tests) that KO's diff engine
  // processes diffs (esp. deletes) monotonically ascending i.e. from index 0 -> N.
  for (var i = this.indexesToDelete.length - 1; i >= 0; --i) {
    this.lastNodesList.splice(this.indexesToDelete[i], 1);
  }
  this.indexesToDelete = [];
};


ko.bindingHandlers.fastForEach = {
  // Valid valueAccessors:
  //    []
  //    ko.observable([])
  //    ko.observableArray([])
  //    ko.computed
  //    {data: array, name: string, as: string}
  init: function init(element, valueAccessor, bindings, vm, context) {
    var value = valueAccessor(),
        ffe;
    if (isPlainObject(value)) {
      value.element = value.element || element;
      value.$context = context;
      ffe = new FastForEach(value);
    } else {
      ffe = new FastForEach({
        element: element,
        data: ko.unwrap(context.$rawData) === value ? context.$rawData : value,
        $context: context
      });
    }
    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
      ffe.dispose();
    });
    return {controlsDescendantBindings: true};
  },

  // Export for testing, debugging, and overloading.
  FastForEach: FastForEach
};

ko.virtualElements.allowedBindings.fastForEach = true;
}));
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/bootstrap',['require','../template/renderer','./i18n','./scope','./range','./mage-init','./keyboard','./optgroup','./after-render','./autoselect','./outer_click','./fadeVisible','./collapsible','./staticChecked','./simple-checked','./bind-html','./tooltip','knockoutjs/knockout-repeat','knockoutjs/knockout-fast-foreach'],function (require) {
    'use strict';

    var renderer = require('../template/renderer');

    renderer.addAttribute('repeat', renderer.handlers.wrapAttribute);

    renderer.addAttribute('outerfasteach', {
        binding: 'fastForEach',
        handler: renderer.handlers.wrapAttribute
    });

    renderer
        .addNode('repeat')
        .addNode('fastForEach');

    return {
        i18n:           require('./i18n'),
        scope:          require('./scope'),
        range:          require('./range'),
        mageInit:       require('./mage-init'),
        keyboard:       require('./keyboard'),
        optgroup:       require('./optgroup'),
        afterRender:     require('./after-render'),
        autoselect:     require('./autoselect'),
        outerClick:     require('./outer_click'),
        fadeVisible:    require('./fadeVisible'),
        collapsible:    require('./collapsible'),
        staticChecked:  require('./staticChecked'),
        simpleChecked:  require('./simple-checked'),
        bindHtml:       require('./bind-html'),
        tooltip:        require('./tooltip'),
        repeat:         require('knockoutjs/knockout-repeat'),
        fastForEach:    require('knockoutjs/knockout-fast-foreach')
    };
});

/*!
 * Knockout ES5 plugin - https://github.com/SteveSanderson/knockout-es5
 * Copyright (c) Steve Sanderson
 * MIT license
 */

(function(global, undefined) {
  'use strict';

  var ko;

  // Model tracking
  // --------------
  //
  // This is the central feature of Knockout-ES5. We augment model objects by converting properties
  // into ES5 getter/setter pairs that read/write an underlying Knockout observable. This means you can
  // use plain JavaScript syntax to read/write the property while still getting the full benefits of
  // Knockout's automatic dependency detection and notification triggering.
  //
  // For comparison, here's Knockout ES3-compatible syntax:
  //
  //     var firstNameLength = myModel.user().firstName().length; // Read
  //     myModel.user().firstName('Bert'); // Write
  //
  // ... versus Knockout-ES5 syntax:
  //
  //     var firstNameLength = myModel.user.firstName.length; // Read
  //     myModel.user.firstName = 'Bert'; // Write

  // `ko.track(model)` converts each property on the given model object into a getter/setter pair that
  // wraps a Knockout observable. Optionally specify an array of property names to wrap; otherwise we
  // wrap all properties. If any of the properties are already observables, we replace them with
  // ES5 getter/setter pairs that wrap your original observable instances. In the case of readonly
  // ko.computed properties, we simply do not define a setter (so attempted writes will be ignored,
  // which is how ES5 readonly properties normally behave).
  //
  // By design, this does *not* recursively walk child object properties, because making literally
  // everything everywhere independently observable is usually unhelpful. When you do want to track
  // child object properties independently, define your own class for those child objects and put
  // a separate ko.track call into its constructor --- this gives you far more control.
  /**
   * @param {object} obj
   * @param {object|array.<string>} propertyNamesOrSettings
   * @param {boolean} propertyNamesOrSettings.deep Use deep track.
   * @param {array.<string>} propertyNamesOrSettings.fields Array of property names to wrap.
   * todo: @param {array.<string>} propertyNamesOrSettings.exclude Array of exclude property names to wrap.
   * todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property 
   *   names to wrap. A function that takes ... params
   * @return {object}
   */
  function track(obj, propertyNamesOrSettings) {
    if (!obj || typeof obj !== 'object') {
      throw new Error('When calling ko.track, you must pass an object as the first parameter.');
    }

    var propertyNames;

    if ( isPlainObject(propertyNamesOrSettings) ) {
      // defaults
      propertyNamesOrSettings.deep = propertyNamesOrSettings.deep || false;
      propertyNamesOrSettings.fields = propertyNamesOrSettings.fields || Object.getOwnPropertyNames(obj);
      propertyNamesOrSettings.lazy = propertyNamesOrSettings.lazy || false;

      wrap(obj, propertyNamesOrSettings.fields, propertyNamesOrSettings);
    } else {
      propertyNames = propertyNamesOrSettings || Object.getOwnPropertyNames(obj);
      wrap(obj, propertyNames, {});
    }

    return obj;
  }

  // fix for ie
  var rFunctionName = /^function\s*([^\s(]+)/;
  function getFunctionName( ctor ){
    if (ctor.name) {
      return ctor.name;
    }
    return (ctor.toString().trim().match( rFunctionName ) || [])[1];
  }

  function canTrack(obj) {
    return obj && typeof obj === 'object' && getFunctionName(obj.constructor) === 'Object';
  }

  function createPropertyDescriptor(originalValue, prop, map) {
    var isObservable = ko.isObservable(originalValue);
    var isArray = !isObservable && Array.isArray(originalValue);
    var observable = isObservable ? originalValue
        : isArray ? ko.observableArray(originalValue)
        : ko.observable(originalValue);

    map[prop] = function () { return observable; };

    // add check in case the object is already an observable array
    if (isArray || (isObservable && 'push' in observable)) {
      notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);
    }

    return {
      configurable: true,
      enumerable: true,
      get: observable,
      set: ko.isWriteableObservable(observable) ? observable : undefined
    };
  }

  function createLazyPropertyDescriptor(originalValue, prop, map) {
    if (ko.isObservable(originalValue)) {
      // no need to be lazy if we already have an observable
      return createPropertyDescriptor(originalValue, prop, map);
    }

    var observable;

    function getOrCreateObservable(value, writing) {
      if (observable) {
        return writing ? observable(value) : observable;
      }

      if (Array.isArray(value)) {
        observable = ko.observableArray(value);
        notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);
        return observable;
      }

      return (observable = ko.observable(value));
    }

    map[prop] = function () { return getOrCreateObservable(originalValue); };
    return {
      configurable: true,
      enumerable: true,
      get: function () { return getOrCreateObservable(originalValue)(); },
      set: function (value) { getOrCreateObservable(value, true); }
    };
  }

  function wrap(obj, props, options) {
    if (!props.length) {
      return;
    }

    var allObservablesForObject = getAllObservablesForObject(obj, true);
    var descriptors = {};

    props.forEach(function (prop) {
      // Skip properties that are already tracked
      if (prop in allObservablesForObject) {
        return;
      }

      // Skip properties where descriptor can't be redefined
      if (Object.getOwnPropertyDescriptor(obj, prop).configurable === false){
        return;
      }

      var originalValue = obj[prop];
      descriptors[prop] = (options.lazy ? createLazyPropertyDescriptor : createPropertyDescriptor)
        (originalValue, prop, allObservablesForObject);

      if (options.deep && canTrack(originalValue)) {
        wrap(originalValue, Object.keys(originalValue), options);
      }
    });

    Object.defineProperties(obj, descriptors);
  }

  function isPlainObject( obj ){
    return !!obj && typeof obj === 'object' && obj.constructor === Object;
  }

  // Lazily created by `getAllObservablesForObject` below. Has to be created lazily because the
  // WeakMap factory isn't available until the module has finished loading (may be async).
  var objectToObservableMap;

  // Gets or creates the hidden internal key-value collection of observables corresponding to
  // properties on the model object.
  function getAllObservablesForObject(obj, createIfNotDefined) {
    if (!objectToObservableMap) {
      objectToObservableMap = weakMapFactory();
    }

    var result = objectToObservableMap.get(obj);
    if (!result && createIfNotDefined) {
      result = {};
      objectToObservableMap.set(obj, result);
    }
    return result;
  }

  // Removes the internal references to observables mapped to the specified properties
  // or the entire object reference if no properties are passed in. This allows the
  // observables to be replaced and tracked again.
  function untrack(obj, propertyNames) {
    if (!objectToObservableMap) {
      return;
    }

    if (arguments.length === 1) {
      objectToObservableMap['delete'](obj);
    } else {
      var allObservablesForObject = getAllObservablesForObject(obj, false);
      if (allObservablesForObject) {
        propertyNames.forEach(function(propertyName) {
          delete allObservablesForObject[propertyName];
        });
      }
    }
  }

  // Computed properties
  // -------------------
  //
  // The preceding code is already sufficient to upgrade ko.computed model properties to ES5
  // getter/setter pairs (or in the case of readonly ko.computed properties, just a getter).
  // These then behave like a regular property with a getter function, except they are smarter:
  // your evaluator is only invoked when one of its dependencies changes. The result is cached
  // and used for all evaluations until the next time a dependency changes).
  //
  // However, instead of forcing developers to declare a ko.computed property explicitly, it's
  // nice to offer a utility function that declares a computed getter directly.

  // Implements `ko.defineProperty`
  function defineComputedProperty(obj, propertyName, evaluatorOrOptions) {
    var ko = this,
      computedOptions = { owner: obj, deferEvaluation: true };

    if (typeof evaluatorOrOptions === 'function') {
      computedOptions.read = evaluatorOrOptions;
    } else {
      if ('value' in evaluatorOrOptions) {
        throw new Error('For ko.defineProperty, you must not specify a "value" for the property. ' +
                        'You must provide a "get" function.');
      }

      if (typeof evaluatorOrOptions.get !== 'function') {
        throw new Error('For ko.defineProperty, the third parameter must be either an evaluator function, ' +
                        'or an options object containing a function called "get".');
      }

      computedOptions.read = evaluatorOrOptions.get;
      computedOptions.write = evaluatorOrOptions.set;
    }

    obj[propertyName] = ko.computed(computedOptions);
    track.call(ko, obj, [propertyName]);
    return obj;
  }

  // Array handling
  // --------------
  //
  // Arrays are special, because unlike other property types, they have standard mutator functions
  // (`push`/`pop`/`splice`/etc.) and it's desirable to trigger a change notification whenever one of
  // those mutator functions is invoked.
  //
  // Traditionally, Knockout handles this by putting special versions of `push`/`pop`/etc. on observable
  // arrays that mutate the underlying array and then trigger a notification. That approach doesn't
  // work for Knockout-ES5 because properties now return the underlying arrays, so the mutator runs
  // in the context of the underlying array, not any particular observable:
  //
  //     // Operates on the underlying array value
  //     myModel.someCollection.push('New value');
  //
  // To solve this, Knockout-ES5 detects array values, and modifies them as follows:
  //  1. Associates a hidden subscribable with each array instance that it encounters
  //  2. Intercepts standard mutators (`push`/`pop`/etc.) and makes them trigger the subscribable
  // Then, for model properties whose values are arrays, the property's underlying observable
  // subscribes to the array subscribable, so it can trigger a change notification after mutation.

  // Given an observable that underlies a model property, watch for any array value that might
  // be assigned as the property value, and hook into its change events
  function notifyWhenPresentOrFutureArrayValuesMutate(ko, observable) {
    var watchingArraySubscription = null;
    ko.computed(function () {
      // Unsubscribe to any earlier array instance
      if (watchingArraySubscription) {
        watchingArraySubscription.dispose();
        watchingArraySubscription = null;
      }

      // Subscribe to the new array instance
      var newArrayInstance = observable();
      if (newArrayInstance instanceof Array) {
        watchingArraySubscription = startWatchingArrayInstance(ko, observable, newArrayInstance);
      }
    });
  }

  // Listens for array mutations, and when they happen, cause the observable to fire notifications.
  // This is used to make model properties of type array fire notifications when the array changes.
  // Returns a subscribable that can later be disposed.
  function startWatchingArrayInstance(ko, observable, arrayInstance) {
    var subscribable = getSubscribableForArray(ko, arrayInstance);
    return subscribable.subscribe(observable);
  }

  // Lazily created by `getSubscribableForArray` below. Has to be created lazily because the
  // WeakMap factory isn't available until the module has finished loading (may be async).
  var arraySubscribablesMap;

  // Gets or creates a subscribable that fires after each array mutation
  function getSubscribableForArray(ko, arrayInstance) {
    if (!arraySubscribablesMap) {
      arraySubscribablesMap = weakMapFactory();
    }

    var subscribable = arraySubscribablesMap.get(arrayInstance);
    if (!subscribable) {
      subscribable = new ko.subscribable();
      arraySubscribablesMap.set(arrayInstance, subscribable);

      var notificationPauseSignal = {};
      wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal);
      addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal);
    }

    return subscribable;
  }

  // After each array mutation, fires a notification on the given subscribable
  function wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal) {
    ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'].forEach(function(fnName) {
      var origMutator = arrayInstance[fnName];
      arrayInstance[fnName] = function() {
        var result = origMutator.apply(this, arguments);
        if (notificationPauseSignal.pause !== true) {
          subscribable.notifySubscribers(this);
        }
        return result;
      };
    });
  }

  // Adds Knockout's additional array mutation functions to the array
  function addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal) {
    ['remove', 'removeAll', 'destroy', 'destroyAll', 'replace'].forEach(function(fnName) {
      // Make it a non-enumerable property for consistency with standard Array functions
      Object.defineProperty(arrayInstance, fnName, {
        enumerable: false,
        value: function() {
          var result;

          // These additional array mutators are built using the underlying push/pop/etc.
          // mutators, which are wrapped to trigger notifications. But we don't want to
          // trigger multiple notifications, so pause the push/pop/etc. wrappers and
          // delivery only one notification at the end of the process.
          notificationPauseSignal.pause = true;
          try {
            // Creates a temporary observableArray that can perform the operation.
            result = ko.observableArray.fn[fnName].apply(ko.observableArray(arrayInstance), arguments);
          }
          finally {
            notificationPauseSignal.pause = false;
          }
          subscribable.notifySubscribers(arrayInstance);
          return result;
        }
      });
    });
  }

  // Static utility functions
  // ------------------------
  //
  // Since Knockout-ES5 sets up properties that return values, not observables, you can't
  // trivially subscribe to the underlying observables (e.g., `someProperty.subscribe(...)`),
  // or tell them that object values have mutated, etc. To handle this, we set up some
  // extra utility functions that can return or work with the underlying observables.

  // Returns the underlying observable associated with a model property (or `null` if the
  // model or property doesn't exist, or isn't associated with an observable). This means
  // you can subscribe to the property, e.g.:
  //
  //     ko.getObservable(model, 'propertyName')
  //       .subscribe(function(newValue) { ... });
  function getObservable(obj, propertyName) {
    if (!obj || typeof obj !== 'object') {
      return null;
    }

    var allObservablesForObject = getAllObservablesForObject(obj, false);
    if (allObservablesForObject && propertyName in allObservablesForObject) {
      return allObservablesForObject[propertyName]();
    }

    return null;
  }
  
  // Returns a boolean indicating whether the property on the object has an underlying
  // observables. This does the check in a way not to create an observable if the
  // object was created with lazily created observables
  function isTracked(obj, propertyName) {
    if (!obj || typeof obj !== 'object') {
      return false;
    }
    
    var allObservablesForObject = getAllObservablesForObject(obj, false);
    return !!allObservablesForObject && propertyName in allObservablesForObject;
  }

  // Causes a property's associated observable to fire a change notification. Useful when
  // the property value is a complex object and you've modified a child property.
  function valueHasMutated(obj, propertyName) {
    var observable = getObservable(obj, propertyName);

    if (observable) {
      observable.valueHasMutated();
    }
  }

  // Module initialisation
  // ---------------------
  //
  // When this script is first evaluated, it works out what kind of module loading scenario
  // it is in (Node.js or a browser `<script>` tag), stashes a reference to its dependencies
  // (currently that's just the WeakMap shim), and then finally attaches itself to whichever
  // instance of Knockout.js it can find.

  // A function that returns a new ES6-compatible WeakMap instance (using ES5 shim if needed).
  // Instantiated by prepareExports, accounting for which module loader is being used.
  var weakMapFactory;

  // Extends a Knockout instance with Knockout-ES5 functionality
  function attachToKo(ko) {
    ko.track = track;
    ko.untrack = untrack;
    ko.getObservable = getObservable;
    ko.valueHasMutated = valueHasMutated;
    ko.defineProperty = defineComputedProperty;

    // todo: test it, maybe added it to ko. directly
    ko.es5 = {
      getAllObservablesForObject: getAllObservablesForObject,
      notifyWhenPresentOrFutureArrayValuesMutate: notifyWhenPresentOrFutureArrayValuesMutate,
      isTracked: isTracked
    };
  }

  // Determines which module loading scenario we're in, grabs dependencies, and attaches to KO
  function prepareExports() {
    if (typeof exports === 'object' && typeof module === 'object') {
      // Node.js case - load KO and WeakMap modules synchronously
      ko = require('knockout');
      var WM = require('../lib/weakmap');
      attachToKo(ko);
      weakMapFactory = function() { return new WM(); };
      module.exports = ko;
    } else if (typeof define === 'function' && define.amd) {
      define('knockoutjs/knockout-es5',['knockout'], function(koModule) {
        ko = koModule;
        attachToKo(koModule);
        weakMapFactory = function() { return new global.WeakMap(); };
        return koModule;
      });
    } else if ('ko' in global) {
      // Non-module case - attach to the global instance, and assume a global WeakMap constructor
      ko = global.ko;
      attachToKo(global.ko);
      weakMapFactory = function() { return new global.WeakMap(); };
    }
  }

  prepareExports();

})(this);
/**
 * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/domReady for details
 */
/*jslint */
/*global require: false, define: false, requirejs: false,
  window: false, clearInterval: false, document: false,
  self: false, setInterval: false */


define('domReady',[],function () {
    'use strict';

    var isTop, testDiv, scrollIntervalId,
        isBrowser = typeof window !== "undefined" && window.document,
        isPageLoaded = !isBrowser,
        doc = isBrowser ? document : null,
        readyCalls = [];

    function runCallbacks(callbacks) {
        var i;
        for (i = 0; i < callbacks.length; i += 1) {
            callbacks[i](doc);
        }
    }

    function callReady() {
        var callbacks = readyCalls;

        if (isPageLoaded) {
            //Call the DOM ready callbacks
            if (callbacks.length) {
                readyCalls = [];
                runCallbacks(callbacks);
            }
        }
    }

    /**
     * Sets the page as loaded.
     */
    function pageLoaded() {
        if (!isPageLoaded) {
            isPageLoaded = true;
            if (scrollIntervalId) {
                clearInterval(scrollIntervalId);
            }

            callReady();
        }
    }

    if (isBrowser) {
        if (document.addEventListener) {
            //Standards. Hooray! Assumption here that if standards based,
            //it knows about DOMContentLoaded.
            document.addEventListener("DOMContentLoaded", pageLoaded, false);
            window.addEventListener("load", pageLoaded, false);
        } else if (window.attachEvent) {
            window.attachEvent("onload", pageLoaded);

            testDiv = document.createElement('div');
            try {
                isTop = window.frameElement === null;
            } catch (e) {}

            //DOMContentLoaded approximation that uses a doScroll, as found by
            //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/,
            //but modified by other contributors, including jdalton
            if (testDiv.doScroll && isTop && window.external) {
                scrollIntervalId = setInterval(function () {
                    try {
                        testDiv.doScroll();
                        pageLoaded();
                    } catch (e) {}
                }, 30);
            }
        }

        //Check if document is no longer loading, and if so, just trigger page load
        //listeners. Latest webkit browsers also use "interactive", and
        //will fire the onDOMContentLoaded before "interactive" but not after
        //entering "interactive" or "complete". More details:
        //http://dev.w3.org/html5/spec/the-end.html#the-end
        //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded
        //Hmm, this is more complicated on further use, see "firing too early"
        //bug: https://github.com/requirejs/domReady/issues/1
        //so removing the || document.readyState === "interactive" test.
        //There is still a window.onload binding that should get fired if
        //DOMContentLoaded is missed.
        if (document.readyState !== "loading") {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            setTimeout(pageLoaded);
        }
    }

    /** START OF PUBLIC API **/

    /**
     * Registers a callback for DOM ready. If DOM is already ready, the
     * callback is called immediately.
     * @param {Function} callback
     */
    function domReady(callback) {
        if (isPageLoaded) {
            callback(doc);
        } else {
            readyCalls.push(callback);
        }
        return domReady;
    }

    domReady.version = '2.0.1';

    /**
     * Loader Plugin API method
     */
    domReady.load = function (name, req, onLoad, config) {
        if (config.isBuild) {
            onLoad(null);
        } else {
            domReady(onLoad);
        }
    };

    /** END OF PUBLIC API **/

    return domReady;
});


/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/** Loads all available knockout bindings, sets custom template engine, initializes knockout on page */

define('Magento_Ui/js/lib/knockout/bootstrap',[
    'ko',
    './template/engine',
    'knockoutjs/knockout-es5',
    './bindings/bootstrap',
    './extender/observable_array',
    './extender/bound-nodes',
    'domReady!'
], function (ko, templateEngine) {
    'use strict';

    ko.uid = 0;

    ko.setTemplateEngine(templateEngine);
    try {
        ko.applyBindings();
    }catch(e) {
        (console.error || console.log)(e);
        window.dataLayer && window.dataLayer.push({
            'event': 'Knockout error',
            'error': {
                'url': window.location.href,
                'message': e.message,
                'stack': e.stack
            }
        });
    }
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/core/renderer/types',[
    'underscore',
    'mageUtils'
], function (_, utils) {
    'use strict';

    var store = {};

    /**
     * Flatten a nested data.
     *
     * @param {Object} data
     * @returns {Object}
     */
    function flatten(data) {
        var extender = data.extends || [],
            result = {};

        extender = utils.stringToArray(extender);

        extender.push(data);

        extender.forEach(function (item) {
            if (_.isString(item)) {
                item = store[item] || {};
            }

            utils.extend(result, item);
        });

        delete result.extends;

        return result;
    }

    return {
        /**
         * Set types to store object.
         *
         * @param {Object} types
         */
        set: function (types) {
            types = types || {};

            utils.extend(store, types);

            _.each(types, function (data, type) {
                store[type] = flatten(data);
            });
        },

        /**
         * Get type from store object.
         *
         * @param {String} type
         * @returns {*|{}}
         */
        get: function (type) {
            return store[type] || {};
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/core/renderer/layout',[
    'underscore',
    'jquery',
    'mageUtils',
    'uiRegistry',
    './types',
    '../../lib/logger/console-logger'
], function (_, $, utils, registry, types, consoleLogger) {
    'use strict';

    var templates = registry.create(),
        layout = {},
        cachedConfig = {};

    /**
     * Build name from parent name and node name
     *
     * @param {Object} parent
     * @param {Object} node
     * @param {String} [name]
     * @returns {String}
     */
    function getNodeName(parent, node, name) {
        var parentName = parent && parent.name;

        if (typeof name !== 'string') {
            name = node.name || name;
        }

        return utils.fullPath(parentName, name);
    }

    /**
     * Get node type from node or parent.
     *
     * @param {Object} parent
     * @param {Object} node
     * @returns {String}
     */
    function getNodeType(parent, node) {
        return node.type || parent && parent.childType;
    }

    /**
     * Get data scope based on parent data scope and node data scope.
     *
     * @param {Object} parent
     * @param {Object} node
     * @returns {String}
     */
    function getDataScope(parent, node) {
        var dataScope = node.dataScope,
            parentScope = parent && parent.dataScope;

        return !utils.isEmpty(parentScope) ?
            !utils.isEmpty(dataScope) ?
                parentScope + '.' + dataScope :
                parentScope :
            dataScope || '';
    }

    /**
     * Load node dependencies on other instances.
     *
     * @param {Object} node
     * @returns {jQueryPromise}
     */
    function loadDeps(node) {
        var loaded = $.Deferred(),
            loggerUtils = consoleLogger.utils;

        if (node.deps) {
            consoleLogger.utils.asyncLog(
                loaded,
                {
                    data: {
                        component: node.name,
                        deps: node.deps
                    },
                    messages: loggerUtils.createMessages(
                        'depsStartRequesting',
                        'depsFinishRequesting',
                        'depsLoadingFail'
                    )
                }
            );
        }

        registry.get(node.deps, function (deps) {
            node.provider = node.extendProvider ? deps && deps.name : node.provider;
            loaded.resolve(node);
        });

        return loaded.promise();
    }

    /**
     * Load node component file via requirejs.
     *
     * @param {Object} node
     * @returns {jQueryPromise}
     */
    function loadSource(node) {
        var loaded = $.Deferred(),
            source = node.component;

        consoleLogger.info('componentStartLoading', {
            component: node.component
        });

        require([source], function (constr) {
            consoleLogger.info('componentFinishLoading', {
                component: node.component
            });
            loaded.resolve(node, constr);
        }, function () {
            consoleLogger.error('componentLoadingFail', {
                component: node.component
            });
        });

        return loaded.promise();
    }

    /**
     * Create a new component instance and set it to the registry.
     *
     * @param {Object} node
     * @param {Function} Constr
     */
    function initComponent(node, Constr) {
        var component = new Constr(_.omit(node, 'children'));

        consoleLogger.info('componentStartInitialization', {
            component: node.component,
            componentName: node.name
        });

        registry.set(node.name, component);
    }

    /**
     * Application entry point.
     *
     * @param {Object} nodes
     * @param {Object} parent
     * @param {Boolean} cached
     * @param {Boolean} merge
     * @returns {Boolean|undefined}
     */
    function run(nodes, parent, cached, merge) {
        if (_.isBoolean(merge) && merge) {
            layout.merge(nodes);

            return false;
        }

        if (cached) {
            cachedConfig[_.keys(nodes)[0]] = JSON.parse(JSON.stringify(nodes));
        }

        _.each(nodes || [], layout.iterator.bind(layout, parent));
    }

    _.extend(layout, {
        /**
         * Determines if node ready to be added or process it.
         *
         * @param {Object} parent
         * @param {Object|String} node
         */
        iterator: function (parent, node) {
            var action = _.isString(node) ?
                this.addChild :
                this.process;

            action.apply(this, arguments);
        },

        /**
         * Prepare component.
         *
         * @param {Object} parent
         * @param {Object} node
         * @param {String} name
         * @returns {Object}
         */
        process: function (parent, node, name) {
            if (!parent && node.parent) {
                return this.waitParent(node, name);
            }

            if (node.nodeTemplate) {
                return this.waitTemplate.apply(this, arguments);
            }

            node = this.build.apply(this, arguments);

            if (!registry.has(node.name)) {
                this.addChild(parent, node)
                    .manipulate(node)
                    .initComponent(node);
            }

            if (node) {
                run(node.children, node);
            }

            return this;
        },

        /**
         * Detailed processing of component config.
         *
         * @param {Object} parent
         * @param {Object} node
         * @param {String} name
         * @returns {Boolean|Object}
         */
        build: function (parent, node, name) {
            var defaults    = parent && parent.childDefaults || {},
                children    = this.filterDisabledChildren(node.children),
                type        = getNodeType(parent, node),
                dataScope   = getDataScope(parent, node),
                component,
                extendDeps  = true,
                nodeName;

            node.children = false;
            node.extendProvider = true;

            if (node.config && node.config.provider || node.provider) {
                node.extendProvider = false;
            }

            if (node.config && node.config.deps || node.deps) {
                extendDeps = false;
            }

            node = utils.extend({
            }, types.get(type), defaults, node);

            nodeName = getNodeName(parent, node, name);

            if (registry.has(nodeName)) {
                component = registry.get(nodeName);
                component.children = children;

                return component;
            }

            if (extendDeps && parent && parent.deps && type) {
                node.deps = parent.deps;
            }

            _.extend(node, node.config || {}, {
                index: node.name || name,
                name: nodeName,
                dataScope: dataScope,
                parentName: utils.getPart(nodeName, -2),
                parentScope: utils.getPart(dataScope, -2)
            });

            node.children = children;
            node.componentType = node.type;

            delete node.type;
            delete node.config;

            if (children) {
                node.initChildCount = _.size(children);
            }

            if (node.isTemplate) {
                node.isTemplate = false;

                templates.set(node.name, node);
                registry.get(node.parentName, function (parentComp) {
                    parentComp.childTemplate = node;
                });

                return false;
            }

            if (node.componentDisabled === true) {
                return false;
            }

            return node;
        },

        /**
         * Filter out all disabled components.
         *
         * @param {Object} children
         * @returns {*}
         */
        filterDisabledChildren: function (children) {
            var cIds;

            //cleanup children config.componentDisabled = true
            if (children && typeof children === 'object') {
                cIds = Object.keys(children);

                if (cIds) {
                    _.each(cIds, function (cId) {
                        if (typeof children[cId] === 'object' &&
                            children[cId].hasOwnProperty('config') &&
                            typeof children[cId].config === 'object' &&
                            children[cId].config.hasOwnProperty('componentDisabled') &&
                            children[cId].config.componentDisabled === true) {
                            delete children[cId];
                        }
                    });
                }
            }

            return children;
        },

        /**
         * Init component.
         *
         * @param {Object} node
         * @returns {Object}
         */
        initComponent: function (node) {
            if (!node.component) {
                return this;
            }

            loadDeps(node)
                .then(loadSource)
                .done(initComponent);

            return this;
        }
    });

    _.extend(layout, {
        /**
         * Loading component marked as isTemplate.
         *
         * @param {Object} parent
         * @param {Object} node
         * @returns {Object}
         */
        waitTemplate: function (parent, node) {
            var args = _.toArray(arguments);

            templates.get(node.nodeTemplate, function () {
                this.applyTemplate.apply(this, args);
            }.bind(this));

            return this;
        },

        /**
         * Waiting for parent component and process provided component.
         *
         * @param {Object} node
         * @param {String} name
         * @returns {Object}
         */
        waitParent: function (node, name) {
            var process = this.process.bind(this);

            registry.get(node.parent, function (parent) {
                process(parent, node, name);
            });

            return this;
        },

        /**
         * Processing component marked as isTemplate.
         *
         * @param {Object} parent
         * @param {Object} node
         * @param {String} name
         */
        applyTemplate: function (parent, node, name) {
            var template = templates.get(node.nodeTemplate);

            node = utils.extend({}, template, node);

            delete node.nodeTemplate;

            this.process(parent, node, name);
        }
    });

    _.extend(layout, {
        /**
         * Determines inserting strategy.
         *
         * @param {Object} node
         * @returns {Object}
         */
        manipulate: function (node) {
            var name = node.name;

            if (node.appendTo) {
                this.insert(name, node.appendTo, -1);
            }

            if (node.prependTo) {
                this.insert(name, node.prependTo, 0);
            }

            if (node.insertTo) {
                this.insertTo(name, node.insertTo);
            }

            return this;
        },

        /**
         * Insert component to provide target and position.
         *
         * @param {Object|String} item
         * @param {Object} target
         * @param {Number} position
         * @returns {Object}
         */
        insert: function (item, target, position) {
            registry.get(target, function (container) {
                container.insertChild(item, position);
            });

            return this;
        },

        /**
         * Insert component into multiple targets.
         *
         * @param {Object} item
         * @param {Array} targets
         * @returns {Object}
         */
        insertTo: function (item, targets) {
            _.each(targets, function (info, target) {
                this.insert(item, target, info.position);
            }, this);

            return this;
        },

        /**
         * Add provided child to parent.
         *
         * @param {Object} parent
         * @param {Object|String} child
         * @returns {Object}
         */
        addChild: function (parent, child) {
            var name;

            if (parent && parent.component) {
                name = child.name || child;

                this.insert(name, parent.name, child.sortOrder);
            }

            return this;
        },

        /**
         * Merge components configuration with cached configuration.
         *
         * @param {Array} components
         */
        merge: function (components) {
            var cachedKey = _.keys(components)[0],
                compared = utils.compare(cachedConfig[cachedKey], components),
                remove = this.filterComponents(this.getByProperty(compared.changes, 'type', 'remove'), true),
                update = this.getByProperty(compared.changes, 'type', 'update'),
                dataSources = this.getDataSources(components),
                names, index, name, component;

            _.each(dataSources, function (val, key) {
                name = key.replace(/\.children|\.config/g, '');
                component = registry.get(name);

                component.cacheData();
                component.updateConfig(
                    true,
                    this.getFullConfig(key, components),
                    this.getFullConfig(key, cachedConfig[cachedKey])
                );
            }, this);

            _.each(remove, function (val) {
                component = registry.get(val.path);

                if (component) {
                    component.destroy();
                }
            });

            update = _.compact(_.filter(update, function (val) {
                return !_.isEqual(val.oldValue, val.value);
            }));

            _.each(update, function (val) {
                names = val.path.split('.');
                index = Math.max(_.lastIndexOf(names, 'config'), _.lastIndexOf(names, 'children') + 2);
                name = _.without(names.splice(0, index), 'children', 'config').join('.');
                component = registry.get(name);

                if (val.name === 'sortOrder' && component) {
                    registry.get(component.parentName).insertChild(component, val.value);
                } else if (component) {
                    component.updateConfig(
                        val.oldValue,
                        val.value,
                        val.path
                    );
                }
            }, this);

            run(components, undefined, true);
        },

        /**
         * Recursive dataSource assignment.
         *
         * @param {Object} config
         * @param {String} parentPath
         * @returns {Object}
         */
        getDataSources: function (config, parentPath) {
            var dataSources = {},
                key, obj;

            /* eslint-disable no-loop-func, max-depth */
            for (key in config) {
                if (config.hasOwnProperty(key)) {
                    if (
                        key === 'type' &&
                        config[key] === 'dataSource' &&
                        config.hasOwnProperty('config')
                    ) {
                        dataSources[parentPath + '.config'] = config.config;
                    } else if (_.isObject(config[key])) {
                        obj = this.getDataSources(config[key], utils.fullPath(parentPath, key));

                        _.each(obj, function (value, path) {
                            dataSources[path] = value;
                        });
                    }
                }
            }

            /* eslint-enable no-loop-func, max-depth */

            return dataSources;
        },

        /**
         * Configuration getter.
         *
         * @param {String} path
         * @param {Object} config
         * @returns {Boolean|Object}
         */
        getFullConfig: function (path, config) {
            var index;

            path = path.split('.');
            index = _.lastIndexOf(path, 'config');

            if (!~index) {
                return false;
            }
            path = path.splice(0, index);

            _.each(path, function (val) {
                config = config[val];
            });

            return config.config;
        },

        /**
         * Filter data by property and value.
         *
         * @param {Object} data
         * @param {String} prop
         * @param {*} propValue
         */
        getByProperty: function (data, prop, propValue) {
            return _.filter(data, function (value) {
                return value[prop] === propValue;
            });
        },

        /**
         * Filter components.
         *
         * @param {Array} data
         * @param {Boolean} splitPath
         * @param {Number} index
         * @param {String} separator
         * @param {String} keyName
         * @returns {Array}
         */
        filterComponents: function (data, splitPath, index, separator, keyName) {
            var result = [],
                names, length;

            index = -2;
            separator = '.' || separator;
            keyName = 'children' || keyName;

            _.each(data, function (val) {
                names = val.path.split(separator);
                length  = names.length;

                if (names[length + index] === keyName) {
                    val.path = splitPath ? _.without(names, keyName).join(separator) : val.path;
                    result.push(val);
                }
            });

            return result;
        }
    });

    return run;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/core/app',[
    './renderer/types',
    './renderer/layout',
    '../lib/knockout/bootstrap'
], function (types, layout) {
    'use strict';

    return function (data, merge) {
        types.set(data.types);
        layout(data.components, undefined, true, merge);
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('js/mixins/sidebar',[
    'jquery',
    'vaimo/overlay',
    'Magento_Ui/js/modal/alert',
    'jquery/ui'
], function ($, overlay, alert) {
    'use strict';

    return function (originalWidget) {
        $.widget(originalWidget.prototype.namespace + '.' + originalWidget.prototype.widgetName,
            $[originalWidget.prototype.namespace][originalWidget.prototype.widgetName],
            {
                _create: function () {
                    this._superApply(arguments);

                    this.element.on('input', this.options.item.qty, (function (e) {
                        let $input = $(e.target);
                        $input
                            .parents(this.options.productItemSelector)
                            .eq(0)
                            .find(this.options.item.qtyText)
                            .text($input.val() || 1);
                    }).bind(this));

                    let self = this,
                        events = {},
                        handlersObjs = [
                            {
                                eventName: 'click ' + self.options.button.remove,
                                handler: function (e) {
                                    e.stopPropagation();
                                    self._removeItem($(e.currentTarget));
                                }
                            },
                            {
                                eventName: 'click ' + self.options.item.button,
                                handler: function (e) {
                                    e.stopPropagation();
                                    self._updateItemQty($(e.currentTarget));
                                }
                            }
                        ];


                    handlersObjs.forEach(function(obj) {
                        events[obj.eventName] = obj.handler;
                    });
                    self._off(self.element, handlersObjs.map(function (obj) { return obj.eventName}).join(','));
                    self._on(self.element, events);
                },

                _removeItemAfter: function (elem) {
                    this._superApply(arguments);

                    elem.css({'pointerEvents': 'none'});
                },

                removeItem: function (id) {
                    this._ajax(this.options.url.remove, {
                        'item_id': id
                    }, $(), this._removeItemAfter);
                },

                updateItemQty: function (id, val, cb) {
                    this._ajax(this.options.url.update, {
                        'item_id': id,
                        'item_qty': val
                    }, $(), function() {
                        cb();
                        this._updateItemQtyAfter.apply(this, arguments);
                    }, cb);
                },

                _ajax: function (url, data, elem, callback, callbackFail) {
                    callbackFail = callbackFail || function(){};
                    callback = callback || function(){};

                    $.extend(data, {
                        'form_key': $.mage.cookies.get('form_key')
                    });

                    $.ajax({
                        url: url,
                        data: data,
                        type: 'post',
                        dataType: 'json',
                        context: this,

                        /** @inheritdoc */
                        beforeSend: function () {
                            elem.attr('disabled', 'disabled');
                            overlay.open('sidebar');
                        },

                        /** @inheritdoc */
                        complete: function () {
                            elem.attr('disabled', null);
                            overlay.close('sidebar');
                        }
                    })
                        .done(function (response) {
                            let msg;

                            if (response.success) {
                                callback.call(this, elem, response);
                            } else {
                                callbackFail.call(this, elem, response);
                                this._onCartActionAjaxFail();
                                msg = response['error_message'];

                                if (msg) {
                                    alert({
                                        content: msg
                                    });
                                }
                            }
                        })
                        .fail(function (error) {
                            callbackFail.call(this, elem, error);
                            window.console.log(JSON.stringify(error));
                            this._onCartActionAjaxFail();
                        });
                },

                _onCartActionAjaxFail: function () {
                    $(document).trigger('sidebarFailedAjax');
                },

                _showItemButton: function (elem) {
                    let itemId = elem.data('cart-item'),
                        itemQty = elem.data('item-qty');

                    if (this._isValidQty(itemQty, elem.val())) {
                        $('#update-cart-item-' + itemId).show();
                        return;
                    }

                    this._hideItemButton(elem);
                },

                _hideItemButton: function (elem) {
                    let itemId = elem.data('cart-item');

                    $('#update-cart-item-' + itemId).hide();
                }
            });

        return $[originalWidget.prototype.namespace][originalWidget.prototype.widgetName];
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */
define('js/mixins/dynamic-components',[],function() {return function(registry) {'use strict';
function initComponent(component, js) {
    require(['Magento_Ui/js/core/app'], function(coreApp) {
        const components = {components: {}};
        components.components[component] = {
            component: js
        };
        coreApp(components);
    })
}
const list = {
    'Banner': initComponent.bind(0, 'Banner', 'Banner'),
    'algolia-simple-html': initComponent.bind(0, 'algolia-simple-html', 'simple-html'),
    'algolia-price': initComponent.bind(0, 'algolia-price', 'AlgoliaPrice')
}
const get = registry.get;
registry.get = function(name) {
    if (typeof list[name] === 'function') {
        list[name]();
        delete list[name];
    }

    return get.apply(registry, arguments)
}
return registry;
}
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('js/mixins/form/element/abstract',['ko', 'Magento_Ui/js/lib/validation/validator', 'uiRegistry'], (ko, validator, registry)=>{'use strict';
return Abstract => {
    return Abstract.extend({
        defaults: {
            listens: {
                '${ $.provider }:${ $.customScope ? $.customScope + "." : ""}data.resetFormBlock': 'reset',
                '${ $.provider }:${ $.customScope ? $.customScope + "." : ""}data.resetInputValidationStarted': 'resetInputValidationStarted'
            }
        },

        initObservable: function () {
            const result = this._super();

            if (this.analyzeThis) {
                this.observe(this.analyzeThis);
            }

            this.observe({'_inputValidationStarted': false});

            this.noError = ko.computed(function () {
                const error = this.error();
                return this._inputValidationStarted() ? !error : false;
            }, this);

            this.doAfterRenderAction = typeof this.afterRenderAction === 'undefined' ? function(){} : this.doAfterRenderAction.bind(this);
            return result;
        },

        initialize: function () {
            const result = this._super();

            if (this.value()) {
                this.validate();
            }

            return result;
        },

        _setClasses: function () {
            const result = this._super();

            this.additionalClasses['_no-error'] = this.noError

            if (this.additionalDynamicClasses) {
                for (let i in this.additionalDynamicClasses) {
                    if (this[this.additionalDynamicClasses[i]]) {
                        this.additionalClasses[i] = this[this.additionalDynamicClasses[i]];
                    }
                }
            }

            return result;
        },

        validate: function () {
            const result = this._super();

            this._inputValidationStarted(!!result.target.value());

            return result;
        },

        resetInputValidationStarted: function() {
            this._inputValidationStarted(false);
            this.error(false);
        },

        satisfyCuriosityForValidationStatus: function () {
            const value = this.value();
            const result = validator(this.validation, value, this.validationParams);
            const isValid = this.disabled() || !this.visible() || result.passed;

            if (!isValid && this.source) {
                this.source.set('params.spyInvalidErrorCollector', Object.assign(this.source.get('params.spyInvalidErrorCollector') || {}, {[this.index]: result}));
                this.source.set('params.spyInvalid', true);
            }

            return {
                valid: isValid,
                target: this
            };
        },

        doAfterRenderAction: function () {
            try {
                const root = window[this.afterRenderAction.root];
                return root[this.afterRenderAction.action].bind(root);
            } catch(e) {return ''}
        },

        onUpdate: function () {
            if (!this.preventUpdateBubble) {
                this.bubble('update', this.hasChanged());
            }

            this.validate();

        }
    });
};
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Customer/js/invalidation-processor',[
    'underscore',
    'uiElement',
    'Magento_Customer/js/customer-data'
], function (_, Element, customerData) {
    'use strict';

    return Element.extend({
        /**
         * Initialize object
         */
        initialize: function () {
            this._super();
            this.process(customerData);
        },

        /**
         * Process all rules in loop, each rule can invalidate some sections in customer data
         *
         * @param {Object} customerDataObject
         */
        process: function (customerDataObject) {
            _.each(this.invalidationRules, function (rule, ruleName) {
                _.each(rule, function (ruleArgs, rulePath) {
                    require([rulePath], function (Rule) {
                        var currentRule = new Rule(ruleArgs);

                        if (!_.isFunction(currentRule.process)) {
                            throw new Error('Rule ' + ruleName + ' should implement invalidationProcessor interface');
                        }
                        currentRule.process(customerDataObject);
                    });
                });
            });
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Persistent/js/view/customer-data-mixin',[
    'jquery',
    'mage/utils/wrapper'
], function ($, wrapper) {
    'use strict';

    var mixin = {

        /**
         * Check if persistent section is expired due to lifetime.
         *
         * @param {Function} originFn - Original method.
         * @return {Array}
         */
        getExpiredSectionNames: function (originFn) {
            var expiredSections = originFn(),
                storage = $.initNamespaceStorage('mage-cache-storage').localStorage,
                currentTimestamp = Math.floor(Date.now() / 1000),
                persistentIndex = expiredSections.indexOf('persistent'),
                persistentLifeTime = 0,
                sectionData;

            if (window.persistent !== undefined && window.persistent.expirationLifetime !== undefined) {
                persistentLifeTime = window.persistent.expirationLifetime;
            }

            if (persistentIndex !== -1) {
                sectionData = storage.get('persistent');

                if (typeof sectionData === 'object' &&
                    sectionData['data_id'] + persistentLifeTime >= currentTimestamp
                ) {
                    expiredSections.splice(persistentIndex, 1);
                }
            }

            return expiredSections;
        },

        /**
         * @param {Object} settings
         * @constructor
         */
        'Magento_Customer/js/customer-data': function (originFn) {
            let mageCacheTimeout = new Date($.localStorage.get('mage-cache-timeout')),
                mageCacheSessId = $.cookieStorage.isSet('mage-cache-sessid');

            originFn();
            if (window.persistent !== undefined && (mageCacheTimeout < new Date() || !mageCacheSessId)) {
                this.reload(['persistent','cart'],true);
            }
        }
    };

    /**
     * Override default customer-data.getExpiredSectionNames().
     */
    return function (target) {
        return wrapper.extend(target, mixin);
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Customer/js/invalidation-rules/website-rule',[
    'uiClass'
], function (Element) {
    'use strict';

    return Element.extend({

        defaults: {
            scopeConfig: {}
        },

        /**
         * Takes website id from current customer data and compare it with current website id
         * If customer belongs to another scope, we need to invalidate current section
         *
         * @param {Object} customerData
         */
        process: function (customerData) {
            var customer = customerData.get('customer');

            if (this.scopeConfig && customer() &&
                ~~customer().websiteId !== ~~this.scopeConfig.websiteId && ~~customer().websiteId !== 0) {
                customerData.reload(['customer']);
            }
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/loader',[
    'jquery',
    'vaimo/overlay',
    'jquery-ui-modules/widget',
    'mage/translate'
], function ($, overlay) {
    'use strict';

    $.widget('mage.loader', {
        loaderStarted: 0,

        /**
         * Loader creation
         * @protected
         */
        _create: function () {
            this._bind();
        },

        /**
         * Bind on ajax events
         * @protected
         */
        _bind: function () {
            this._on({
                'processStop': 'hide',
                'processStart': 'show',
                'show.loader': 'show',
                'hide.loader': 'hide',
                'contentUpdated.loader': '_contentUpdated'
            });
        },

        /**
         * Verify loader present after content updated
         *
         * This will be cleaned up by the task MAGETWO-11070
         *
         * @param {EventObject} e
         * @private
         */
        _contentUpdated: function (e) {
            this.show(e);
        },

        /**
         * Show loader
         */
        show: function (e, ctx) {
            this.loaderStarted++;
            overlay.open();

            return false;
        },

        /**
         * Hide loader
         */
        hide: function () {
            if (this.loaderStarted > 0) {
                this.loaderStarted--;

                if (this.loaderStarted === 0) {
                    overlay.close();
                }
            }

            return false;
        },

        /**
         * Destroy loader
         */
        _destroy: function () {
            this.spinner.remove();
        }
    });

    /**
     * This widget takes care of registering the needed loader listeners on the body
     */
    $.widget('mage.loaderAjax', {
        options: {
            defaultContainer: '[data-container=body]',
            loadingClass: 'ajax-loading'
        },

        /**
         * @private
         */
        _create: function () {
            this._bind();
            // There should only be one instance of this widget, and it should be attached
            // to the body only. Having it on the page twice will trigger multiple processStarts.
            if (window.console && !this.element.is(this.options.defaultContainer) && $.mage.isDevMode(undefined)) {
                console.warn('This widget is intended to be attached to the body, not below.');
            }
        },

        /**
         * @private
         */
        _bind: function () {
            $(document).on({
                'ajaxSend': this._onAjaxSend.bind(this),
                'ajaxComplete': this._onAjaxComplete.bind(this)
            });
        },

        /**
         * @param {Object} loaderContext
         * @return {*}
         * @private
         */
        _getJqueryObj: function (loaderContext) {
            var ctx;

            // Check to see if context is jQuery object or not.
            if (loaderContext) {
                if (loaderContext.jquery) {
                    ctx = loaderContext;
                } else {
                    ctx = $(loaderContext);
                }
            } else {
                ctx = $('[data-container="body"]');
            }

            return ctx;
        },

        /**
         * @param {jQuery.Event} e
         * @param {Object} jqxhr
         * @param {Object} settings
         * @private
         */
        _onAjaxSend: function (e, jqxhr, settings) {
            var ctx;

            $(this.options.defaultContainer)
                .addClass(this.options.loadingClass)
                .attr({
                    'aria-busy': true
                });

            if (settings && settings.showLoader) {
                ctx = this._getJqueryObj(settings.loaderContext);
                ctx.trigger('processStart');

                // Check to make sure the loader is there on the page if not report it on the console.
                // NOTE that this check should be removed before going live. It is just an aid to help
                // in finding the uses of the loader that maybe broken.
                if (window.console && !ctx.parents('[data-role="loader"]').length) {
                    console.warn('Expected to start loader but did not find one in the dom');
                }
            }
        },

        /**
         * @param {jQuery.Event} e
         * @param {Object} jqxhr
         * @param {Object} settings
         * @private
         */
        _onAjaxComplete: function (e, jqxhr, settings) {
            $(this.options.defaultContainer)
                .removeClass(this.options.loadingClass)
                .attr('aria-busy', false);

            if (settings && settings.showLoader) {
                this._getJqueryObj(settings.loaderContext).trigger('processStop');
            }
        }

    });

    return {
        loader: $.mage.loader,
        loaderAjax: $.mage.loaderAjax
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/bootstrap',[
    'jquery',
    'mage/apply/main',
    'Magento_Ui/js/lib/knockout/bootstrap'
], function ($, mage) {
    'use strict';

    $.ajaxSetup({
        cache: false
    });

    /**
     * Init all components defined via data-mage-init attribute.
     * Execute in a separate task to prevent main thread blocking.
     */
    setTimeout(mage.apply);
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/common',[
    'jquery',
    'domReady!'
], function ($) {
    'use strict';

    /* Form with auto submit feature */
    $('form[data-auto-submit="true"]').trigger('submit');

    //Add form keys.
    $(document).on(
        'submit',
        'form',
        function (e) {
            var formKeyElement,
                existingFormKeyElement,
                isKeyPresentInForm,
                isActionExternal,
                baseUrl = window.BASE_URL,
                form = $(e.target),
                formKey = $('input[name="form_key"]').val(),
                formMethod = form.prop('method'),
                formAction = form.prop('action');

            isActionExternal = formAction.indexOf(baseUrl) !== 0;

            existingFormKeyElement = form.find('input[name="form_key"]');
            isKeyPresentInForm = existingFormKeyElement.length;

            /* Verifies that existing auto-added form key is a direct form child element,
               protection from a case when one form contains another form. */
            if (isKeyPresentInForm && existingFormKeyElement.attr('auto-added-form-key') === '1') {
                isKeyPresentInForm = form.find('> input[name="form_key"]').length;
            }

            if (formKey && !isKeyPresentInForm && !isActionExternal && formMethod !== 'get') {
                formKeyElement = document.createElement('input');
                formKeyElement.setAttribute('type', 'hidden');
                formKeyElement.setAttribute('name', 'form_key');
                formKeyElement.setAttribute('value', formKey);
                formKeyElement.setAttribute('auto-added-form-key', '1');
                form.get(0).appendChild(formKeyElement);
            }
        }
    );
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/smart-keyboard-handler',[
    'jquery'
], function ($) {
    'use strict';

    /**
     * @return {Object}
     * @constructor
     */
    function KeyboardHandler() {
        var body = $('body'),
            focusState = false,
            tabFocusClass = '_keyfocus',
            productsGrid = '[data-container="product-grid"]',
            catalogProductsGrid = $(productsGrid),
            CODE_TAB = 9;

        /**
         * Handle logic, when onTabKeyPress fired at first.
         * Then it changes state.
         */
        function onFocusInHandler() {
            focusState = true;
            body.addClass(tabFocusClass)
                .off('focusin.keyboardHandler', onFocusInHandler);
        }

        /**
         * Handle logic to remove state after onTabKeyPress to normal.
         */
        function onClickHandler() {
            focusState = false;
            body.removeClass(tabFocusClass)
                .off('click', onClickHandler);
        }

        /**
         * Tab key onKeypress handler. Apply main logic:
         *  - call differ actions onTabKeyPress and onClick
         */
        function smartKeyboardFocus() {
            $(document).on('keydown keypress', function (event) {
                if (event.which === CODE_TAB && !focusState) {
                    body
                        .on('focusin.keyboardHandler', onFocusInHandler)
                        .on('click', onClickHandler);
                }
            });

            // ARIA support for catalog grid products
            if (catalogProductsGrid.length) {
                body.on('focusin.gridProducts', productsGrid, function () {
                    if (body.hasClass(tabFocusClass)) {
                        $(this).addClass('active');
                    }
                });
                body.on('focusout.gridProducts', productsGrid, function () {
                    $(this).removeClass('active');
                });
            }
        }

        /**
         * Attach smart focus on specific element.
         * @param {jQuery} element
         */
        function handleFocus(element) {
            element.on('focusin.emulateTabFocus', function () {
                focusState = true;
                body.addClass(tabFocusClass);
                element.off();
            });

            element.on('focusout.emulateTabFocus', function () {
                focusState = false;
                body.removeClass(tabFocusClass);
                element.off();
            });
        }

        return {
            apply: smartKeyboardFocus,
            focus: handleFocus
        };
    }

    return new KeyboardHandler;
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/tabs',[
    'jquery',
    'jquery-ui-modules/widget',
    'jquery/ui-modules/widgets/tabs',
    'mage/mage',
    'mage/collapsible'
], function ($) {
    'use strict';

    $.widget('mage.tabs', {
        options: {
            active: 0,
            disabled: [],
            openOnFocus: true,
            collapsible: false,
            collapsibleElement: '[data-role=collapsible]',
            header: '[data-role=title]',
            content: '[data-role=content]',
            trigger: '[data-role=trigger]',
            closedState: null,
            openedState: null,
            disabledState: null,
            ajaxUrlElement: '[data-ajax=true]',
            ajaxContent: false,
            loadingClass: null,
            saveState: false,
            animate: false,
            icons: {
                activeHeader: null,
                header: null
            }
        },

        /**
         * @private
         */
        _create: function () {
            if (typeof this.options.disabled === 'string') {
                this.options.disabled = this.options.disabled.split(' ').map(function (item) {
                    return parseInt(item, 10);
                });
            }
            this._processPanels();
            this._handleDeepLinking();
            this._processTabIndex();
            this._closeOthers();
            this._bind();
        },

        /**
         * @private
         */
        _destroy: function () {
            $.each(this.collapsibles, function () {
                $(this).collapsible('destroy');
            });
        },

        /**
         * If deep linking is used, all sections must be closed but the one that contains the anchor.
         * @private
         */
        _handleDeepLinking: function () {
            var self = this,
                anchor = window.location.hash,
                isValid = $.mage.isValidSelector(anchor),
                anchorId = anchor.replace('#', '');

            if (anchor && isValid) {
                $.each(self.contents, function (i) {
                    if ($(this).attr('id') === anchorId || $(this).find('#' + anchorId).length) {
                        self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate');

                        return false;
                    }
                });
            }
        },

        /**
         * When the widget gets instantiated, the first tab that is not disabled receive focusable property
         * All tabs receive tabIndex 0
         * @private
         */
        _processTabIndex: function () {
            var self = this;

            self.triggers.attr('tabIndex', 0);
            $.each(this.collapsibles, function (i) {
                self.triggers.attr('tabIndex', 0);
                self.triggers.eq(i).attr('tabIndex', 0);
            });
        },

        /**
         * Prepare the elements for instantiating the collapsible widget
         * @private
         */
        _processPanels: function () {
            var isNotNested = this._isNotNested.bind(this);

            this.contents = this.element
                .find(this.options.content)
                .filter(isNotNested);

            this.collapsibles =  this.element
                .find(this.options.collapsibleElement)
                .filter(isNotNested);

            this.collapsibles
                .attr('role', 'presentation')
                .parent()
                .attr('role', 'tablist');

            this.headers = this.element
                .find(this.options.header)
                .filter(isNotNested);

            if (this.headers.length === 0) {
                this.headers = this.collapsibles;
            }
            this.triggers = this.element
                .find(this.options.trigger)
                .filter(isNotNested);

            if (this.triggers.length === 0) {
                this.triggers = this.headers;
            }
            this._callCollapsible();
        },

        /**
         * Checks if element is not in nested container to keep the correct scope of collapsible
         * @param {Number} index
         * @param {HTMLElement} element
         * @private
         * @return {Boolean}
         */
        _isNotNested: function (index, element) {
            var parentContent = $(element).parents(this.options.content);

            return !parentContent.length || !this.element.find(parentContent).length;
        },

        /**
         * Setting the disabled and active tabs and calling instantiation of collapsible
         * @private
         */
        _callCollapsible: function () {
            var self = this,
                disabled = false,
                active = false;

            $.each(this.collapsibles, function (i) {
                disabled = active = false;

                if ($.inArray(i, self.options.disabled) !== -1) {
                    disabled = true;
                }

                if (i === self.options.active) {
                    active = true;
                }
                self._instantiateCollapsible(this, i, active, disabled);
            });
        },

        /**
         * Instantiate collapsible.
         *
         * @param {HTMLElement} element
         * @param {Number} index
         * @param {*} active
         * @param {*} disabled
         * @private
         */
        _instantiateCollapsible: function (element, index, active, disabled) {
            $(element).collapsible(
                $.extend({}, this.options, {
                    active: active,
                    disabled: disabled,
                    header: this.headers.eq(index),
                    content: this.contents.eq(index),
                    trigger: this.triggers.eq(index)
                })
            );
        },

        /**
         * Adding callback to close others tabs when one gets opened
         * @private
         */
        _closeOthers: function () {
            var self = this;

            $.each(this.collapsibles, function () {
                $(this).on('beforeOpen', function () {
                    self.collapsibles.not(this).collapsible('forceDeactivate');
                });
            });
        },

        /**
         * @param {*} index
         */
        activate: function (index) {
            this._toggleActivate('activate', index);
        },

        /**
         * @param {*} index
         */
        deactivate: function (index) {
            this._toggleActivate('deactivate', index);
        },

        /**
         * @param {*} action
         * @param {*} index
         * @private
         */
        _toggleActivate: function (action, index) {
            this.collapsibles.eq(index).collapsible(action);
        },

        /**
         * @param {*} index
         */
        disable: function (index) {
            this._toggleEnable('disable', index);
        },

        /**
         * @param {*} index
         */
        enable: function (index) {
            this._toggleEnable('enable', index);
        },

        /**
         * @param {*} action
         * @param {*} index
         * @private
         */
        _toggleEnable: function (action, index) {
            var self = this;

            if (Array.isArray(index)) {
                $.each(index, function () {
                    self.collapsibles.eq(this).collapsible(action);
                });
            } else if (index === undefined) {
                this.collapsibles.collapsible(action);
            } else {
                this.collapsibles.eq(index).collapsible(action);
            }
        },

        /**
         * @param {jQuery.Event} event
         * @private
         */
        _keydown: function (event) {
            var self = this,
                keyCode, toFocus, toFocusIndex, enabledTriggers, length, currentIndex, nextToFocus;

            if (event.altKey || event.ctrlKey) {
                return;
            }
            keyCode = $.ui.keyCode;
            toFocus = false;
            enabledTriggers = [];

            $.each(this.triggers, function () {
                if (!self.collapsibles.eq(self.triggers.index($(this))).collapsible('option', 'disabled')) {
                    enabledTriggers.push(this);
                }
            });
            length = $(enabledTriggers).length;
            currentIndex = $(enabledTriggers).index(event.target);

            /**
             * @param {String} direction
             * @return {*}
             */
            nextToFocus = function (direction) {
                if (length > 0) {
                    if (direction === 'right') {
                        toFocusIndex = (currentIndex + 1) % length;
                    } else {
                        toFocusIndex = (currentIndex + length - 1) % length;
                    }

                    return enabledTriggers[toFocusIndex];
                }

                return event.target;
            };

            switch (event.keyCode) {
                case keyCode.RIGHT:
                case keyCode.DOWN:
                    toFocus = nextToFocus('right');
                    break;

                case keyCode.LEFT:
                case keyCode.UP:
                    toFocus = nextToFocus('left');
                    break;

                case keyCode.HOME:
                    toFocus = enabledTriggers[0];
                    break;

                case keyCode.END:
                    toFocus = enabledTriggers[length - 1];
                    break;
            }

            if (toFocus) {
                toFocusIndex = this.triggers.index(toFocus);
                $(event.target).attr('tabIndex', -1);
                $(toFocus).attr('tabIndex', 0);
                toFocus.focus();

                if (this.options.openOnFocus) {
                    this.activate(toFocusIndex);
                }
                event.preventDefault();
            }
        },

        /**
         * @private
         */
        _bind: function () {
            var events = {
                keydown: '_keydown'
            };

            this._off(this.triggers);
            this._on(this.triggers, events);
        }
    });

    return $.mage.tabs;
});

/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. MIT license */

window.matchMedia || (window.matchMedia = function() {
    "use strict";

    // For browsers that support matchMedium api such as IE 9 and webkit
    var styleMedia = (window.styleMedia || window.media);

    // For those that don't support matchMedium
    if (!styleMedia) {
        var style       = document.createElement('style'),
            script      = document.getElementsByTagName('script')[0],
            info        = null;

        style.type  = 'text/css';
        style.id    = 'matchmediajs-test';

        if (!script) {
            document.head.appendChild(style);
        } else {
            script.parentNode.insertBefore(style, script);
        }

        // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
        info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;

        styleMedia = {
            matchMedium: function(media) {
                var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';

                // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
                if (style.styleSheet) {
                    style.styleSheet.cssText = text;
                } else {
                    style.textContent = text;
                }

                // Test if media query is true or false
                return info.width === '1px';
            }
        };
    }

    return function(media) {
        return {
            matches: styleMedia.matchMedium(media || 'all'),
            media: media || 'all'
        };
    };
}());

/*! matchMedia() polyfill addListener/removeListener extension. Author & copyright (c) 2012: Scott Jehl. Dual MIT/BSD license */
(function() {
    // Bail out for browsers that have addListener support
    if (window.matchMedia && window.matchMedia('all').addListener) {
        return false;
    }

    var localMatchMedia = window.matchMedia,
        hasMediaQueries = localMatchMedia('only all').matches,
        isListening = false,
        timeoutID = 0, // setTimeout for debouncing 'handleChange'
        queries = [], // Contains each 'mql' and associated 'listeners' if 'addListener' is used
        handleChange = function(evt) {
            // Debounce
            clearTimeout(timeoutID);

            timeoutID = setTimeout(function() {
                for (var i = 0, il = queries.length; i < il; i++) {
                    var mql = queries[i].mql,
                        listeners = queries[i].listeners || [],
                        matches = localMatchMedia(mql.media).matches;

                    // Update mql.matches value and call listeners
                    // Fire listeners only if transitioning to or from matched state
                    if (matches !== mql.matches) {
                        mql.matches = matches;

                        for (var j = 0, jl = listeners.length; j < jl; j++) {
                            listeners[j].call(window, mql);
                        }
                    }
                }
            }, 30);
        };

    window.matchMedia = function(media) {
        var mql = localMatchMedia(media),
            listeners = [],
            index = 0;

        mql.addListener = function(listener) {
            // Changes would not occur to css media type so return now (Affects IE <= 8)
            if (!hasMediaQueries) {
                return;
            }

            // Set up 'resize' listener for browsers that support CSS3 media queries (Not for IE <= 8)
            // There should only ever be 1 resize listener running for performance
            if (!isListening) {
                isListening = true;
                window.addEventListener('resize', handleChange, true);
            }

            // Push object only if it has not been pushed already
            if (index === 0) {
                index = queries.push({
                    mql: mql,
                    listeners: listeners
                });
            }

            listeners.push(listener);
        };

        mql.removeListener = function(listener) {
            for (var i = 0, il = listeners.length; i < il; i++) {
                if (listeners[i] === listener) {
                    listeners.splice(i, 1);
                }
            }
        };

        return mql;
    };
}());

window.mediaCheck = function(options) {
    var mq;

    function mqChange(mq, options) {
        if (mq.matches) {
            if (typeof options.entry === "function") {
                options.entry();
            }
        } else if (typeof options.exit === "function") {
            options.exit();
        }
    };

    mq = window.matchMedia(options.media);

    mq.addListener(function() {
        mqChange(mq, options);
    });

    mqChange(mq, options);
};

define("matchMedia", (function (global) {
    return function () {
        var ret, fn;
        return ret || global.mediaCheck;
    };
}(this)));

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/form/element/abstract',[
    'underscore',
    'mageUtils',
    'uiLayout',
    'uiElement',
    'Magento_Ui/js/lib/validation/validator'
], function (_, utils, layout, Element, validator) {
    'use strict';

    return Element.extend({
        defaults: {
            visible: true,
            preview: '',
            focused: false,
            required: false,
            disabled: false,
            valueChangedByUser: false,
            elementTmpl: 'ui/form/element/input',
            tooltipTpl: 'ui/form/element/helper/tooltip',
            fallbackResetTpl: 'ui/form/element/helper/fallback-reset',
            'input_type': 'input',
            placeholder: false,
            description: '',
            labelVisible: true,
            label: '',
            error: '',
            warn: '',
            notice: '',
            customScope: '',
            default: '',
            isDifferedFromDefault: false,
            showFallbackReset: false,
            additionalClasses: {},
            isUseDefault: '',
            serviceDisabled: false,
            valueUpdate: false, // ko binding valueUpdate

            switcherConfig: {
                component: 'Magento_Ui/js/form/switcher',
                name: '${ $.name }_switcher',
                target: '${ $.name }',
                property: 'value'
            },
            listens: {
                visible: 'setPreview',
                value: 'setDifferedFromDefault',
                '${ $.provider }:data.reset': 'reset',
                '${ $.provider }:data.overload': 'overload',
                '${ $.provider }:${ $.customScope ? $.customScope + "." : ""}data.validate': 'validate',
                'isUseDefault': 'toggleUseDefault'
            },
            ignoreTmpls: {
                value: true
            },

            links: {
                value: '${ $.provider }:${ $.dataScope }'
            }
        },

        /**
         * Invokes initialize method of parent class,
         * contains initialization logic
         */
        initialize: function () {
            _.bindAll(this, 'reset');

            this._super()
                .setInitialValue()
                ._setClasses()
                .initSwitcher();

            return this;
        },

        /**
         * Checks if component has error.
         *
         * @returns {Object}
         */
        checkInvalid: function () {
            return this.error() && this.error().length ? this : null;
        },

        /**
         * Initializes observable properties of instance
         *
         * @returns {Abstract} Chainable.
         */
        initObservable: function () {
            var rules = this.validation = this.validation || {};

            this._super();

            this.observe('error disabled focused preview visible value warn notice isDifferedFromDefault')
                .observe('isUseDefault serviceDisabled')
                .observe({
                    'required': !!rules['required-entry']
                });

            return this;
        },

        /**
         * Initializes regular properties of instance.
         *
         * @returns {Abstract} Chainable.
         */
        initConfig: function () {
            var uid = utils.uniqueid(),
                name,
                valueUpdate,
                scope;

            this._super();

            scope = this.dataScope.split('.');
            name = scope.length > 1 ? scope.slice(1) : scope;

            valueUpdate = this.showFallbackReset ? 'afterkeydown' : this.valueUpdate;

            _.extend(this, {
                uid: uid,
                noticeId: 'notice-' + uid,
                errorId: 'error-' + uid,
                tooltipId: 'tooltip-' + uid,
                inputName: utils.serializeName(name.join('.')),
                valueUpdate: valueUpdate
            });

            return this;
        },

        /**
         * Initializes switcher element instance.
         *
         * @returns {Abstract} Chainable.
         */
        initSwitcher: function () {
            if (this.switcherConfig.enabled) {
                layout([this.switcherConfig]);
            }

            return this;
        },

        /**
         * Sets initial value of the element and subscribes to it's changes.
         *
         * @returns {Abstract} Chainable.
         */
        setInitialValue: function () {
            this.initialValue = this.getInitialValue();

            if (this.value.peek() !== this.initialValue) {
                this.value(this.initialValue);
            }

            this.on('value', this.onUpdate.bind(this));
            this.isUseDefault(this.disabled());

            return this;
        },

        /**
         * Extends 'additionalClasses' object.
         *
         * @returns {Abstract} Chainable.
         */
        _setClasses: function () {
            var additional = this.additionalClasses;

            if (_.isString(additional)) {
                this.additionalClasses = {};

                if (additional.trim().length) {
                    additional = additional.trim().split(' ');

                    additional.forEach(function (name) {
                        if (name.length) {
                            this.additionalClasses[name] = true;
                        }
                    }, this);
                }
            }

            _.extend(this.additionalClasses, {
                _required: this.required,
                _error: this.error,
                _warn: this.warn,
                _disabled: this.disabled
            });

            return this;
        },

        /**
         * Gets initial value of element
         *
         * @returns {*} Elements' value.
         */
        getInitialValue: function () {
            var values = [this.value(), this.default],
                value;

            values.some(function (v) {
                if (v !== null && v !== undefined) {
                    value = v;

                    return true;
                }

                return false;
            });

            return this.normalizeData(value);
        },

        /**
         * Sets 'value' as 'hidden' property's value, triggers 'toggle' event,
         * sets instance's hidden identifier in params storage based on
         * 'value'.
         *
         * @returns {Abstract} Chainable.
         */
        setVisible: function (isVisible) {
            this.visible(isVisible);

            return this;
        },

        /**
         * Show element.
         *
         * @returns {Abstract} Chainable.
         */
        show: function () {
            this.visible(true);

            return this;
        },

        /**
         * Hide element.
         *
         * @returns {Abstract} Chainable.
         */
        hide: function () {
            this.visible(false);

            return this;
        },

        /**
         * Disable element.
         *
         * @returns {Abstract} Chainable.
         */
        disable: function () {
            this.disabled(true);

            return this;
        },

        /**
         * Enable element.
         *
         * @returns {Abstract} Chainable.
         */
        enable: function () {
            this.disabled(false);

            return this;
        },

        /**
         *
         * @param {(String|Object)} rule
         * @param {(Object|Boolean)} [options]
         * @returns {Abstract} Chainable.
         */
        setValidation: function (rule, options) {
            var rules = utils.copy(this.validation),
                changed;

            if (_.isObject(rule)) {
                _.extend(this.validation, rule);
            } else {
                this.validation[rule] = options;
            }

            changed = !utils.compare(rules, this.validation).equal;

            if (changed) {
                this.required(!!rules['required-entry']);
                this.validate();
            }

            return this;
        },

        /**
         * Returns unwrapped preview observable.
         *
         * @returns {String} Value of the preview observable.
         */
        getPreview: function () {
            return this.value();
        },

        /**
         * Checks if element has addons
         *
         * @returns {Boolean}
         */
        hasAddons: function () {
            return this.addbefore || this.addafter;
        },

        /**
         * Checks if element has service setting
         *
         * @returns {Boolean}
         */
        hasService: function () {
            return this.service && this.service.template;
        },

        /**
         * Defines if value has changed.
         *
         * @returns {Boolean}
         */
        hasChanged: function () {
            var notEqual = this.value() !== this.initialValue;

            return !this.visible() ? false : notEqual;
        },

        /**
         * Checks if 'value' is not empty.
         *
         * @returns {Boolean}
         */
        hasData: function () {
            return !utils.isEmpty(this.value());
        },

        /**
         * Sets value observable to initialValue property.
         *
         * @returns {Abstract} Chainable.
         */
        reset: function () {
            this.value(this.initialValue);
            this.error(false);

            return this;
        },

        /**
         * Sets current state as initial.
         */
        overload: function () {
            this.setInitialValue();
            this.bubble('update', this.hasChanged());
        },

        /**
         * Clears 'value' property.
         *
         * @returns {Abstract} Chainable.
         */
        clear: function () {
            this.value('');

            return this;
        },

        /**
         * Converts values like 'null' or 'undefined' to an empty string.
         *
         * @param {*} value - Value to be processed.
         * @returns {*}
         */
        normalizeData: function (value) {
            return utils.isEmpty(value) ? '' : value;
        },

        /**
         * Validates itself by it's validation rules using validator object.
         * If validation of a rule did not pass, writes it's message to
         * 'error' observable property.
         *
         * @returns {Object} Validate information.
         */
        validate: function () {
            var value = this.value(),
                result = validator(this.validation, value, this.validationParams),
                message = !this.disabled() && this.visible() ? result.message : '',
                isValid = this.disabled() || !this.visible() || result.passed;

            this.error(message);
            this.error.valueHasMutated();
            this.bubble('error', message);

            //TODO: Implement proper result propagation for form
            if (this.source && !isValid) {
                this.source.set('params.invalid', true);
            }

            return {
                valid: isValid,
                target: this
            };
        },

        /**
         * Callback that fires when 'value' property is updated.
         */
        onUpdate: function () {
            this.bubble('update', this.hasChanged());

            this.validate();
        },

        /**
         * Restore value to default
         */
        restoreToDefault: function () {
            this.value(this.default);
            this.focused(true);
        },

        /**
         * Update whether value differs from default value
         */
        setDifferedFromDefault: function () {
            var value = typeof this.value() != 'undefined' && this.value() !== null ? this.value() : '',
                defaultValue = typeof this.default != 'undefined' && this.default !== null ? this.default : '';

            this.isDifferedFromDefault(value !== defaultValue);
        },

        /**
         * @param {Boolean} state
         */
        toggleUseDefault: function (state) {
            this.disabled(state);

            if (this.source && this.hasService()) {
                this.source.set('data.use_default.' + this.index, Number(state));
            }
        },

        /**
         *  Callback when value is changed by user
         */
        userChanges: function () {
            this.valueChangedByUser = true;
        },

        /**
         * Returns correct id for 'aria-describedby' accessibility attribute
         *
         * @returns {Boolean|String}
         */
        getDescriptionId: function () {
            var id = false;

            if (this.error()) {
                id = this.errorId;
            } else if (this.notice()) {
                id = this.noticeId;
            }

            return id;
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_PageCache/js/form-key-provider',[],function () {
    'use strict';

    return function (settings) {
        var formKey,
            inputElements,
            inputSelector = 'input[name="form_key"]';

        /**
         * Set form_key cookie
         * @private
         */
        function setFormKeyCookie(value) {
            var expires,
                secure,
                date = new Date(),
                cookiesConfig = window.cookiesConfig || {},
                isSecure = !!cookiesConfig.secure,
                samesite = cookiesConfig.samesite || 'lax';

            date.setTime(date.getTime() + 86400000);
            expires = '; expires=' + date.toUTCString();
            secure = isSecure ? '; secure' : '';
            samesite = '; samesite=' + samesite;

            document.cookie = 'form_key=' + (value || '') + expires + secure + '; path=/' + samesite;
        }

        /**
         * Retrieves form key from cookie
         * @private
         */
        function getFormKeyCookie() {
            var cookie,
                i,
                nameEQ = 'form_key=',
                cookieArr = document.cookie.split(';');

            for (i = 0; i < cookieArr.length; i++) {
                cookie = cookieArr[i];

                while (cookie.charAt(0) === ' ') {
                    cookie = cookie.substring(1, cookie.length);
                }

                if (cookie.indexOf(nameEQ) === 0) {
                    return cookie.substring(nameEQ.length, cookie.length);
                }
            }

            return null;
        }

        /**
         * Get form key from UI input hidden
         * @private
         */
        function getFormKeyFromUI() {
            return document.querySelector(inputSelector).value;
        }

        /**
         * Generate form key string
         * @private
         */
        function generateFormKeyString() {
            var result = '',
                length = 16,
                chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

            while (length--) {
                result += chars[Math.round(Math.random() * (chars.length - 1))];
            }

            return result;
        }

        /**
         * Init form_key inputs with value
         * @private
         */
        function initFormKey() {
            formKey = getFormKeyCookie();

            if (settings && settings.isPaginationCacheEnabled && !formKey) {
                formKey = getFormKeyFromUI();
                setFormKeyCookie(formKey);
            }

            if (!formKey) {
                formKey = generateFormKeyString();
                setFormKeyCookie(formKey);
            }
            inputElements = document.querySelectorAll(inputSelector);

            if (inputElements.length) {
                Array.prototype.forEach.call(inputElements, function (element) {
                    element.setAttribute('value', formKey);
                });
            }
        }

        initFormKey();
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_PurchaseOrderRule/js/validation/messages',[
    'jquery',
    'mage/mage',
    'mage/translate'
], function ($) {
    'use strict';

    $('.form-create-purchase-order-rule').mage('validation', {
        messages: {
            'conditions[0][value]': {
                'validate-digits': $.mage.__('Please enter a whole number.')
            }
        }
    });
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('js/mixins/menu-mixin',['jquery', 'vaimo/headerState'], function ($, headerState) {
    'use strict';

    return function (Menu) {
        Menu.prototype._onMenuRequested = (function (parentMethod) {
            return function () {
                parentMethod.apply(this, arguments);
                headerState.subscribe("isSearchPopupOpened", this.hide.bind(this));
            }
        })(Menu.prototype._onMenuRequested);

        Menu.prototype._onTopLevelClicked = (function (parentMethod) {
            return function ($foldEl, isOpen) {
                parentMethod.apply(this, arguments);
                this._handleHeaderState(!isOpen);
            }
        })(Menu.prototype._onTopLevelClicked);

        Menu.prototype._beforeResize = (function (parentMethod) {
            return function () {
                parentMethod.apply(this, arguments);
                headerState.setState('isMobileMenu', this._isMob());
            }
        })(Menu.prototype._beforeResize);

        Menu.prototype._handleHeaderState = function(isOpen) {
            headerState.setState('isMenuActive', isOpen);
        };

        return Menu;
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * A loose JavaScript version of Magento\Framework\Escaper
 *
 * Due to differences in how XML/HTML is processed in PHP vs JS there are a couple of minor differences in behavior
 * from the PHP counterpart.
 *
 * The first difference is that the default invocation of escapeHtml without allowedTags will double-escape existing
 * entities as the intention of such an invocation is that the input isn't supposed to contain any HTML.
 *
 * The second difference is that escapeHtml will not escape quotes. Since the input is actually being processed by the
 * DOM there is no chance of quotes being mixed with HTML syntax. And, since escapeHtml is not
 * intended to be used with raw injection into a HTML attribute, this is acceptable.
 *
 * @api
 */
define('Magento_Security/js/escaper',[], function () {
    'use strict';

    return {
        neverAllowedElements: ['script', 'img', 'embed', 'iframe', 'video', 'source', 'object', 'audio'],
        generallyAllowedAttributes: ['id', 'class', 'href', 'title', 'style'],
        forbiddenAttributesByElement: {
            a: ['style']
        },

        /**
         * Escape a string for safe injection into HTML
         *
         * @param {String} data
         * @param {Array|null} allowedTags
         * @returns {String}
         */
        escapeHtml: function (data, allowedTags) {
            var domParser = new DOMParser(),
                fragment = domParser.parseFromString('<div></div>', 'text/html');

            fragment = fragment.body.childNodes[0];
            allowedTags = typeof allowedTags === 'object' && allowedTags.length ? allowedTags : null;

            if (allowedTags) {
                fragment.innerHTML = data || '';
                allowedTags = this._filterProhibitedTags(allowedTags);

                this._removeComments(fragment);
                this._removeNotAllowedElements(fragment, allowedTags);
                this._removeNotAllowedAttributes(fragment);

                return fragment.innerHTML;
            }

            fragment.textContent = data || '';

            return fragment.innerHTML;
        },

        /**
         * Remove the always forbidden tags from a list of provided tags
         *
         * @param {Array} tags
         * @returns {Array}
         * @private
         */
        _filterProhibitedTags: function (tags) {
            return tags.filter(function (n) {
                return this.neverAllowedElements.indexOf(n) === -1;
            }.bind(this));
        },

        /**
         * Remove comment nodes from the given node
         *
         * @param {Node} node
         * @private
         */
        _removeComments: function (node) {
            var treeWalker = node.ownerDocument.createTreeWalker(
                    node,
                    NodeFilter.SHOW_COMMENT,
                    function () {
                        return NodeFilter.FILTER_ACCEPT;
                    },
                    false
                ),
                nodesToRemove = [];

            while (treeWalker.nextNode()) {
                nodesToRemove.push(treeWalker.currentNode);
            }

            nodesToRemove.forEach(function (nodeToRemove) {
                nodeToRemove.parentNode.removeChild(nodeToRemove);
            });
        },

        /**
         * Strip the given node of all disallowed tags while permitting any nested text nodes
         *
         * @param {Node} node
         * @param {Array|null} allowedTags
         * @private
         */
        _removeNotAllowedElements: function (node, allowedTags) {
            var treeWalker = node.ownerDocument.createTreeWalker(
                    node,
                    NodeFilter.SHOW_ELEMENT,
                    function (currentNode) {
                        return allowedTags.indexOf(currentNode.nodeName.toLowerCase()) === -1 ?
                            NodeFilter.FILTER_ACCEPT
                            // SKIP instead of REJECT because REJECT also rejects child nodes
                            : NodeFilter.FILTER_SKIP;
                    },
                false
                ),
                nodesToRemove = [];

            while (treeWalker.nextNode()) {
                if (allowedTags.indexOf(treeWalker.currentNode.nodeName.toLowerCase()) === -1) {
                    nodesToRemove.push(treeWalker.currentNode);
                }
            }

            nodesToRemove.forEach(function (nodeToRemove) {
                nodeToRemove.parentNode.replaceChild(
                    node.ownerDocument.createTextNode(nodeToRemove.textContent),
                    nodeToRemove
                );
            });
        },

        /**
         * Remove any invalid attributes from the given node
         *
         * @param {Node} node
         * @private
         */
        _removeNotAllowedAttributes: function (node) {
            var treeWalker = node.ownerDocument.createTreeWalker(
                    node,
                    NodeFilter.SHOW_ELEMENT,
                    function () {
                        return NodeFilter.FILTER_ACCEPT;
                    },
                false
                ),
                i,
                attribute,
                nodeName,
                attributesToRemove = [];

            while (treeWalker.nextNode()) {
                for (i = 0; i < treeWalker.currentNode.attributes.length; i++) {
                    attribute = treeWalker.currentNode.attributes[i];
                    nodeName = treeWalker.currentNode.nodeName.toLowerCase();

                    if (this.generallyAllowedAttributes.indexOf(attribute.name) === -1  || // eslint-disable-line max-depth,max-len
                        this._checkHrefValue(attribute) ||
                        this.forbiddenAttributesByElement[nodeName] &&
                        this.forbiddenAttributesByElement[nodeName].indexOf(attribute.name) !== -1
                    ) {
                        attributesToRemove.push(attribute);
                    }
                }
            }

            attributesToRemove.forEach(function (attributeToRemove) {
                attributeToRemove.ownerElement.removeAttribute(attributeToRemove.name);
            });
        },

        /**
         * Check that attribute contains script content
         *
         * @param {Object} attribute
         * @private
         */
        _checkHrefValue: function (attribute) {
            return attribute.nodeName === 'href' && attribute.nodeValue.startsWith('javascript');
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_PageCache/js/page-cache',[
    'jquery',
    'domReady',
    'consoleLogger',
    'Magento_PageCache/js/form-key-provider',
    'jquery-ui-modules/widget',
    'mage/cookies'
], function ($, domReady, consoleLogger, formKeyInit) {
    'use strict';

    /**
     * Helper. Generate random string
     * TODO: Merge with mage/utils
     * @param {String} chars - list of symbols
     * @param {Number} length - length for need string
     * @returns {String}
     */
    function generateRandomString(chars, length) {
        var result = '';

        length = length > 0 ? length : 1;

        while (length--) {
            result += chars[Math.round(Math.random() * (chars.length - 1))];
        }

        return result;
    }

    /**
     * Nodes tree to flat list converter
     * @returns {Array}
     */
    $.fn.comments = function () {
        var elements = [],
            contents,
            elementContents;

        /**
         * @param {jQuery} element - Comment holder
         */
        (function lookup(element) {
            var iframeHostName;

            // prevent cross origin iframe content reading
            if ($(element).prop('tagName') === 'IFRAME') {
                iframeHostName = $('<a>').prop('href', $(element).prop('src'))
                    .prop('hostname');

                if (window.location.hostname !== iframeHostName) {
                    return [];
                }
            }

            /**
             * Rewrite jQuery contents().
             *
             * @param {jQuery} elem
             */
            contents = function (elem) {
                return $.map(elem, function (el) {
                    try {
                        return el.nodeName.toLowerCase() === 'iframe' ?
                            el.contentDocument || (el.contentWindow ? el.contentWindow.document : []) :
                            $.merge([], el.childNodes);
                    } catch (e) {
                        consoleLogger.error(e);

                        return [];
                    }
                });
            };

            elementContents = contents($(element));

            $.each(elementContents, function (index, el) {
                switch (el.nodeType) {
                    case 1: // ELEMENT_NODE
                        lookup(el);
                        break;

                    case 8: // COMMENT_NODE
                        elements.push(el);
                        break;

                    case 9: // DOCUMENT_NODE
                        lookup($(el).find('body'));
                        break;
                }
            });
        })(this);

        return elements;
    };

    /**
     * FormKey Widget - this widget is generating from key, saves it to cookie and
     * @deprecated see Magento/PageCache/view/frontend/web/js/form-key-provider.js
     */
    $.widget('mage.formKey', {
        options: {
            inputSelector: 'input[name="form_key"]',
            allowedCharacters: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
            length: 16
        },

        /**
         * Creates widget 'mage.formKey'
         * @private
         */
        _create: function () {
            var formKey = $.mage.cookies.get('form_key'),
                options = {
                    secure: window.cookiesConfig ? window.cookiesConfig.secure : false
                };

            if (!formKey) {
                formKey = generateRandomString(this.options.allowedCharacters, this.options.length);
                $.mage.cookies.set('form_key', formKey, options);
            }
            $(this.options.inputSelector).val(formKey);
        }
    });

    /**
     * PageCache Widget
     * Handles additional ajax request for rendering user private content.
     */
    $.widget('mage.pageCache', {
        options: {
            url: '/',
            patternPlaceholderOpen: /^ BLOCK (.+) $/,
            patternPlaceholderClose: /^ \/BLOCK (.+) $/,
            versionCookieName: 'private_content_version',
            handles: []
        },

        /**
         * Creates widget 'mage.pageCache'
         * @private
         */
        _create: function () {
            var placeholders,
                version = $.mage.cookies.get(this.options.versionCookieName);

            if (!version) {
                return;
            }
            placeholders = this._searchPlaceholders(this.element.comments());

            if (placeholders && placeholders.length) {
                this._ajax(placeholders, version);
            }
        },

        /**
         * Parse page for placeholders.
         * @param {Array} elements
         * @returns {Array}
         * @private
         */
        _searchPlaceholders: function (elements) {
            var placeholders = [],
                tmp = {},
                ii,
                len,
                el, matches, name;

            if (!(elements && elements.length)) {
                return placeholders;
            }

            for (ii = 0, len = elements.length; ii < len; ii++) {
                el = elements[ii];
                matches = this.options.patternPlaceholderOpen.exec(el.nodeValue);
                name = null;

                if (matches) {
                    name = matches[1];
                    tmp[name] = {
                        name: name,
                        openElement: el
                    };
                } else {
                    matches = this.options.patternPlaceholderClose.exec(el.nodeValue);

                    if (matches) { //eslint-disable-line max-depth
                        name = matches[1];

                        if (tmp[name]) { //eslint-disable-line max-depth
                            tmp[name].closeElement = el;
                            placeholders.push(tmp[name]);
                            delete tmp[name];
                        }
                    }
                }
            }

            return placeholders;
        },

        /**
         * Parse for page and replace placeholders
         * @param {Object} placeholder
         * @param {Object} html
         * @protected
         */
        _replacePlaceholder: function (placeholder, html) {
            var startReplacing = false,
                prevSibling = null,
                parent, contents, yy, len, element;

            if (!placeholder || !html) {
                return;
            }

            parent = $(placeholder.openElement).parent();
            contents = parent.contents();

            for (yy = 0, len = contents.length; yy < len; yy++) {
                element = contents[yy];

                if (element == placeholder.openElement) { //eslint-disable-line eqeqeq
                    startReplacing = true;
                }

                if (startReplacing) {
                    $(element).remove();
                } else if (element.nodeType != 8) { //eslint-disable-line eqeqeq
                    //due to comment tag doesn't have siblings we try to find it manually
                    prevSibling = element;
                }

                if (element == placeholder.closeElement) { //eslint-disable-line eqeqeq
                    break;
                }
            }

            if (prevSibling) {
                $(prevSibling).after(html);
            } else {
                $(parent).prepend(html);
            }

            // trigger event to use mage-data-init attribute
            $(parent).trigger('contentUpdated');
        },

        /**
         * AJAX helper
         * @param {Object} placeholders
         * @param {String} version
         * @private
         */
        _ajax: function (placeholders, version) {
            var ii,
                data = {
                    blocks: [],
                    handles: this.options.handles,
                    originalRequest: this.options.originalRequest,
                    version: version
                };

            for (ii = 0; ii < placeholders.length; ii++) {
                data.blocks.push(placeholders[ii].name);
            }
            data.blocks = JSON.stringify(data.blocks.sort());
            data.handles = JSON.stringify(data.handles);
            data.originalRequest = JSON.stringify(data.originalRequest);
            $.ajax({
                url: this.options.url,
                data: data,
                type: 'GET',
                cache: true,
                dataType: 'json',
                context: this,

                /**
                 * Response handler
                 * @param {Object} response
                 */
                success: function (response) {
                    var placeholder, i;

                    for (i = 0; i < placeholders.length; i++) {
                        placeholder = placeholders[i];

                        if (response.hasOwnProperty(placeholder.name)) {
                            this._replacePlaceholder(placeholder, response[placeholder.name]);
                        }
                    }
                }
            });
        }
    });

    domReady(function () {
        formKeyInit();
    });

    return {
        'pageCache': $.mage.pageCache,
        'formKey': $.mage.formKey
    };
});

/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

define('Magento_Customer/js/view/customer',[
    'uiComponent',
    'Magento_Customer/js/customer-data'
], function (Component, customerData) {
    'use strict';

    return Component.extend({
        /** @inheritdoc */
        initialize: function () {
            this._super();

            this.customer = customerData.get('customer');
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Wishlist/js/view/wishlist',[
    'uiComponent',
    'Magento_Customer/js/customer-data'
], function (Component, customerData) {
    'use strict';

    return Component.extend({
        /** @inheritdoc */
        initialize: function () {
            this._super();

            this.wishlist = customerData.get('wishlist');
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Catalog/js/view/image',[
    'uiComponent'
], function (Component) {
    'use strict';

    return Component.extend({
        /** @inheritdoc */
        initialize: function () {
            this._super();

            this.template = window.checkout.imageTemplate || this.template;
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Catalog/js/product/storage/ids-storage',[
    'jquery',
    'underscore',
    'ko',
    'mageUtils',
    'jquery/jquery-storageapi'
], function ($, _, ko, utils) {
    'use strict';

    /**
     * Set data to localStorage with support check.
     *
     * @param {String} namespace
     * @param {Object} data
     */
    function setLocalStorageItem(namespace, data) {
        try {
            window.localStorage.setItem(namespace, JSON.stringify(data));
        } catch (e) {
            console.warn('localStorage is unavailable - skipping local caching of product data');
            console.error(e);
        }
    }

    return {

        /**
         * Class name
         */
        name: 'IdsStorage',

        /**
         * Initializes class
         *
         * @return Chainable.
         */
        initialize: function () {
            if (!this.data) {
                this.data = ko.observable({});
            }

            this.initCustomerDataReloadListener()
                .initLocalStorage()
                .cachesDataFromLocalStorage()
                .initDataListener();

            return this;
        },

        /**
         * Gets data from local storage by current namespace
         *
         * @return {Object}.
         */
        getDataFromLocalStorage: function () {
            return this.localStorage.get();
        },

        /**
         * Caches data from local storage to local scope
         *
         * @return Chainable.
         */
        cachesDataFromLocalStorage: function () {
            this.data(this.getDataFromLocalStorage());

            return this;
        },

        /**
         * Initialize localStorage
         *
         * @return Chainable.
         */
        initLocalStorage: function () {
            this.localStorage = $.initNamespaceStorage(this.namespace).localStorage;

            return this;
        },

        /**
         * Initializes listener to "data" property
         */
        initDataListener: function () {
            this.data.subscribe(this.internalDataHandler.bind(this));
        },

        /**
         * Initialize listener to customer data reload
         *
         * @return Chainable.
         */
        initCustomerDataReloadListener: function () {
            $(document).on('customer-data-reload', function (event, sections) {
                if ((_.isEmpty(sections) || _.contains(sections, this.namespace)) && ~~this.allowToSendRequest) {
                    this.localStorage.removeAll();
                    this.data();
                }
            }.bind(this));

            return this;
        },

        /**
         * Initializes handler to "data" property update
         */
        internalDataHandler: function (data) {
            setLocalStorageItem(this.namespace, data);
        },

        /**
         * Initializes handler to storage update
         */
        externalDataHandler: function (data) {
            data = data.items ? data.items : data;

            this.set(_.extend(utils.copy(this.data()), data));
        }
    };
});


/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Catalog/js/product/query-builder',[
        'underscore'
    ], function (_) {
        'use strict';

        return {

            /**
             * Build query to get id
             *
             * @param {Object} data
             */
            buildQuery: function (data) {
                var filters = [];

                _.each(data, function (value, key) {
                    filters.push({
                        field: key,
                        value: value,
                        'condition_type': 'in'
                    });
                });

                return {
                    searchCriteria: {
                        filterGroups: [
                            {
                                filters: filters
                            }
                        ]
                    }
                };
            }
        };
    }
);

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Catalog/js/product/storage/data-storage',[
    'jquery',
    'underscore',
    'ko',
    'mageUtils',
    'Magento_Catalog/js/product/query-builder',
    'Magento_Customer/js/customer-data',
    'jquery/jquery-storageapi'
], function ($, _, ko, utils, queryBuilder, customerData) {
    'use strict';

    /**
     * Process data from API request
     *
     * @param {Object} data
     * @returns {Object}
     */
    function getParsedDataFromServer(data) {
        var result = {};

        _.each(data.items, function (item) {
                if (item.id) {
                    result[item.id] = item;
                }
            }
        );

        return {
            items: result
        };
    }

    /**
     * Set data to localStorage with support check.
     *
     * @param {String} namespace
     * @param {Object} data
     */
    function setLocalStorageItem(namespace, data) {
        try {
            window.localStorage.setItem(namespace, JSON.stringify(data));
        } catch (e) {
            console.warn('localStorage is unavailable - skipping local caching of product data');
            console.error(e);
        }
    }

    return {

        /**
         * Class name
         */
        name: 'DataStorage',
        request: {},
        customerDataProvider: 'product_data_storage',

        /**
         * Initialize class
         *
         * @return Chainable.
         */
        initialize: function () {
            if (!this.data) {
                this.data = ko.observable({});
            }

            this.initLocalStorage()
                .initCustomerDataReloadListener()
                .cachesDataFromLocalStorage()
                .initDataListener()
                .initProvideStorage()
                .initProviderListener();

            return this;
        },

        /**
         * Initialize listener to customer data reload
         *
         * @return Chainable.
         */
        initCustomerDataReloadListener: function () {
            $(document).on('customer-data-invalidate', this._flushProductStorage.bind(this));

            return this;
        },

        /**
         * Flush product storage
         *
         * @private
         * @return void
         */
        _flushProductStorage: function (event, sections) {
            if (_.isEmpty(sections) || _.contains(sections, 'product_data_storage')) {
                this.localStorage.removeAll();
            }
        },

        /**
         * Initialize listener to data property
         *
         * @return Chainable.
         */
        initDataListener: function () {
            this.data.subscribe(this.dataHandler.bind(this));

            return this;
        },

        /**
         * Initialize provider storage
         *
         * @return Chainable.
         */
        initProvideStorage: function () {
            this.providerHandler(customerData.get(this.customerDataProvider)());

            return this;
        },

        /**
         * Handler to update "data" property.
         * Sets data to localStorage
         *
         * @param {Object} data
         */
        dataHandler: function (data) {
            if (_.isEmpty(data)) {
                this.localStorage.removeAll();
            } else {
                setLocalStorageItem(this.namespace, data);
            }
        },

        /**
         * Handler to update data in provider.
         *
         * @param {Object} data
         */
        providerHandler: function (data) {
            var currentData = utils.copy(this.data()),
                ids = _.keys(data.items);

            if (data.items && ids.length) {
                //we can extend only items
                data = data.items;
                this.data(_.extend(currentData, data));
            }
        },

        /**
         * Sets data ids
         *
         * @param {String} currency
         * @param {String} store
         * @param {Object} ids
         */
        setIds: function (currency, store, ids) {
            if (!this.hasInCache(currency, store, ids)) {
                this.loadDataFromServer(currency, store, ids);
            } else {
                this.data.valueHasMutated();
            }
        },

        /**
         * Gets data from "data" property by identifiers
         *
         * @param {String} currency
         * @param {String} store
         * @param {Object} productIdentifiers
         *
         * @return {Object} data.
         */
        getDataByIdentifiers: function (currency, store, productIdentifiers) {
            var data = {},
                dataCollection = this.data(),
                id;

            for (id in productIdentifiers) {
                if (productIdentifiers.hasOwnProperty(id)) {
                    data[id] = dataCollection[id];
                }
            }

            return data;
        },

        /**
         * Checks has cached data or not
         *
         * @param {String} currency
         * @param {String} store
         * @param {Object} ids
         *
         * @return {Boolean}
         */
        hasInCache: function (currency, store, ids) {
            var data = this.data(),
                id;

            for (id in ids) {
                if (!data.hasOwnProperty(id) ||
                    data[id]['currency_code'] !== currency ||
                    ~~data[id]['store_id'] !== ~~store
                ) {
                    return false;
                }
            }

            return true;
        },

        /**
         * Load data from server by ids
         *
         * @param {String} currency
         * @param {String} store
         * @param {Object} ids
         *
         * @return void
         */
        loadDataFromServer: function (currency, store, ids) {
            var idsArray = _.keys(ids),
                prepareAjaxParams = {
                    'entity_id': idsArray.join(',')
                };

            if (this.request.sent && this.hasIdsInSentRequest(ids)) {
                return;
            }

            this.request = {
                sent: true,
                data: ids
            };

            this.updateRequestConfig.data = queryBuilder.buildQuery(prepareAjaxParams);
            this.updateRequestConfig.data['store_id'] = store;
            this.updateRequestConfig.data['currency_code'] = currency;
            $.ajax(this.updateRequestConfig).done(function (data) {
                this.request = {};
                this.providerHandler(getParsedDataFromServer(data));
            }.bind(this));
        },

        /**
         * Each product page consist product cache data,
         * this function prepare those data to appropriate view, and save it
         *
         * @param {Object} data
         */
        addDataFromPageCache: function (data) {
            this.providerHandler(getParsedDataFromServer(data));
        },

        /**
         * @param {Object} ids
         * @returns {Boolean}
         */
        hasIdsInSentRequest: function (ids) {
            var sentDataIds,
                currentDataIds;

            if (this.request.data) {
                sentDataIds = _.keys(this.request.data);
                currentDataIds = _.keys(ids);

                return _.every(currentDataIds, function (id) {
                    return _.lastIndexOf(sentDataIds, id) !== -1;
                });
            }

            return false;
        },

        /**
         * Initialize provider listener
         *
         * @return Chainable.
         */
        initProviderListener: function () {
            customerData.get(this.customerDataProvider).subscribe(this.providerHandler.bind(this));

            return this;
        },

        /**
         * Caches data from local storage to local scope
         *
         * @return Chainable.
         */
        cachesDataFromLocalStorage: function () {
            this.data(this.getDataFromLocalStorage());

            return this;
        },

        /**
         * Gets data from local storage by current namespace
         *
         * @return {Object}.
         */
        getDataFromLocalStorage: function () {
            return this.localStorage.get();
        },

        /**
         * Initialize localStorage
         *
         * @return Chainable.
         */
        initLocalStorage: function () {
            this.localStorage = $.initNamespaceStorage(this.namespace).localStorage;

            return this;
        }
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Catalog/js/product/storage/ids-storage-compare',[
    'underscore',
    'ko',
    'mageUtils',
    'Magento_Customer/js/customer-data',
    'Magento_Catalog/js/product/storage/ids-storage'
], function (_, ko, utils, customerData, idsStorage) {
    'use strict';

    return _.extend(utils.copy(idsStorage), {

        /**
         * Class name
         */
        name: 'IdsStorageCompare',

        /**
         * Initializes class
         *
         * @return Chainable.
         */
        initialize: function () {
            if (!this.data) {
                this.data = ko.observable({});
            }

            if (this.provider && window.checkout && window.checkout.baseUrl) {
                this.providerDataHandler(customerData.get(this.provider)());
                this.initProviderListener();
            }

            this.initLocalStorage()
                .cachesDataFromLocalStorage()
                .initDataListener();

            return this;
        },

        /**
         * Initializes listener for external data provider
         */
        initProviderListener: function () {
            customerData.get(this.provider).subscribe(this.providerDataHandler.bind(this));
        },

        /**
         * Initializes handler for external data provider update
         *
         * @param {Object} data
         */
        providerDataHandler: function (data) {
            data = data.items || data;
            data = this.prepareData(data);

            this.add(data);
        },

        /**
         * Prepares data to correct interface
         *
         * @param {Object} data
         *
         * @returns {Object} data
         */
        prepareData: function (data) {
            var result = {},
                scopeId;

            _.each(data, function (item) {
                if (typeof item.productScope !== 'undefined') {
                    scopeId = item.productScope === 'store' ? window.checkout.storeId :
                        item.productScope === 'group' ? window.checkout.storeGroupId :
                            window.checkout.websiteId;

                    result[item.productScope + '-' + scopeId + '-' + item.id] = {
                        'added_at': new Date().getTime() / 1000,
                        'product_id': item.id,
                        'scope_id': scopeId
                    };
                } else {
                    result[item.id] = {
                        'added_at': new Date().getTime() / 1000,
                        'product_id': item.id
                    };
                }
            });

            return result;
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Catalog/js/product/storage/storage-service',[
    'jquery',
    'underscore',
    'mageUtils',
    'mage/translate',
    'Magento_Catalog/js/product/storage/ids-storage',
    'Magento_Catalog/js/product/storage/data-storage',
    'Magento_Catalog/js/product/storage/ids-storage-compare'
], function ($, _, utils, $t, IdsStorage, DataStore, IdsStorageCompare) {
    'use strict';

    return (function () {

        var /**
             * {Object} storages - list of storages
             */
            storages = {},

            /**
             * {Object} classes - list of classes
             */
            classes = {},

            /**
             * {Object} prototype - methods that will be added to all storage classes to prototype property.
             */
            prototype = {

                /**
                 * Sets data to storage
                 *
                 * @param {*} data
                 */
                set: function (data) {
                    if (!utils.compare(data, this.data()).equal) {
                        this.data(data);
                    }
                },

                /**
                 * Adds some data to current storage data
                 *
                 * @param {*} data
                 */
                add: function (data) {
                    if (!_.isEmpty(data)) {
                        this.data(_.extend(utils.copy(this.data()), data));
                    }
                },

                /**
                 * Gets current storage data
                 *
                 * @returns {*} data
                 */
                get: function () {
                    return this.data();
                }
            },

            /**
             * Required properties to storage
             */
            storagesInterface =  {
                data: 'function',
                initialize: 'function',
                namespace: 'string'
            },

            /**
             * Private service methods
             */
            _private = {

                /**
                 * Overrides class method and add ability use _super to call parent method
                 *
                 * @param {Object} extensionMethods
                 * @param {Object} originInstance
                 */
                overrideClassMethods: function (extensionMethods, originInstance) {
                    var methodsName = _.keys(extensionMethods),
                        i = 0,
                        length = methodsName.length;

                    for (i; i < length; i++) {
                        if (_.isFunction(originInstance[methodsName[i]])) {
                            originInstance[methodsName[i]] = extensionMethods[methodsName[i]];
                        }
                    }

                    return originInstance;
                },

                /**
                 * Checks is storage implement interface
                 *
                 * @param {Object} classInstance
                 *
                 * @returns {Boolean}
                 */
                isImplementInterface: function (classInstance) {
                    _.each(storagesInterface, function (key, value) {
                        if (typeof classInstance[key] !== value) {
                            return false;
                        }
                    });

                    return true;
                }
            },

            /**
             * Subscribers list
             */
            subsctibers = {};

        (function () {
            /**
             * @param {Object} config
             * @return void
             */
            classes[IdsStorage.name] = function (config) {
                _.extend(this, IdsStorage, config);
            };

            /**
             * @param {Object} config
             * @return void
             */
            classes[IdsStorageCompare.name] = function (config) {
                _.extend(this, IdsStorageCompare, config);
            };

            /**
             * @param {Object} config
             * @return void
             */
            classes[DataStore.name] = function (config) {
                _.extend(this, DataStore, config);
            };

            _.each(classes, function (classItem) {
                classItem.prototype = prototype;
            });
        })();

        return {

            /**
             * Creates new storage or returns if storage with declared namespace exist
             *
             * @param {Object} config - storage config
             * @throws {Error}
             * @returns {Object} storage instance
             */
            createStorage: function (config) {
                var instance,
                    initialized;

                if (storages[config.namespace]) {
                    return storages[config.namespace];
                }

                instance = new classes[config.className](config);

                if (_private.isImplementInterface(instance)) {
                    initialized = storages[config.namespace] = instance.initialize();
                    this.processSubscribers(initialized, config);

                    return initialized;
                }

                throw new Error('Class ' + config.className + $t('does not implement Storage Interface'));
            },

            /**
             * Process subscribers
             *
             * Differentiate subscribers by their namespaces: recently_viewed or recently_compared
             * and process callbacks. Callbacks can be add through onStorageInit function
             *
             * @param {Object} initialized
             * @param {Object} config
             * @return void
             */
            processSubscribers: function (initialized, config) {
                if (subsctibers[config.namespace]) {
                    _.each(subsctibers[config.namespace], function (callback) {
                        callback(initialized);
                    });

                    delete subsctibers[config.namespace];
                }
            },

            /**
             * Listens storage creating by namespace
             *
             * @param {String} namespace
             * @param {Function} callback
             * @return void
             */
            onStorageInit: function (namespace, callback) {
                if (storages[namespace]) {
                    callback(storages[namespace]);
                } else {
                    subsctibers[namespace] ?
                        subsctibers[namespace].push(callback) :
                        subsctibers[namespace] = [callback];
                }
            },

            /**
             * Gets storage by namespace
             *
             * @param {String} namespace
             *
             * @returns {Object} storage insance
             */
            getStorage: function (namespace) {
                return storages[namespace];
            }
        };
    })();
});


/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Catalog/js/storage-manager',[
    'underscore',
    'uiElement',
    'mageUtils',
    'Magento_Catalog/js/product/storage/storage-service',
    'Magento_Customer/js/section-config',
    'jquery'
], function (_, Element, utils, storage, sectionConfig, $) {
    'use strict';

    /**
     * Flush events, that are clones of the same customer data sections
     * Events listener
     */
    $(document).on('submit', function (event) {
        var sections;

        if (event.target.method.match(/post|put|delete/i)) {
            sections = sectionConfig.getAffectedSections(event.target.action);

            if (sections && window.localStorage) {
                _.each(sections, function (section) {
                    window.localStorage.removeItem(section);
                });
            }
        }
    });

    return Element.extend({
        defaults: {
            defaultNamespace: {
                lifetime: 1000
            },
            storagesConfiguration: {
                'recently_viewed_product': {
                    namespace: 'recently_viewed_product',
                    className: 'IdsStorage',
                    lifetime: '${ $.defaultNamespace.lifetime }',
                    requestConfig: {
                        typeId: '${ $.storagesConfiguration.recently_viewed_product.namespace }'
                    },
                    savePrevious: {
                        namespace: '${ $.storagesConfiguration.recently_viewed_product.namespace }' + '_previous',
                        className: '${ $.storagesConfiguration.recently_viewed_product.className }'
                    },
                    allowToSendRequest: 0
                },
                'recently_compared_product': {
                    namespace: 'recently_compared_product',
                    className: 'IdsStorageCompare',
                    provider: 'compare-products',
                    lifetime: '${ $.defaultNamespace.lifetime }',
                    requestConfig: {
                        typeId: '${ $.storagesConfiguration.recently_compared_product.namespace }'
                    },
                    savePrevious: {
                        namespace: '${ $.storagesConfiguration.recently_compared_product.namespace }' + '_previous',
                        className: '${ $.storagesConfiguration.recently_compared_product.className }'
                    },
                    allowToSendRequest: 0
                },
                'product_data_storage': {
                    namespace: 'product_data_storage',
                    className: 'DataStorage',
                    allowToSendRequest: 0,
                    updateRequestConfig: {
                        url: '',
                        method: 'GET',
                        dataType: 'json'
                    }
                }
            },
            requestConfig: {
                method: 'POST',
                dataType: 'json',
                ajaxSaveType: 'default',
                ignoreProcessEvents: true
            },
            requestSent: 0
        },

        /**
         * Initializes provider component.
         *
         * @returns {Object} Chainable.
         */
        initialize: function () {
            this._super()
                .prepareStoragesConfig()
                .initStorages()
                .initStartData()
                .initUpdateStorageDataListener();

            return this;
        },

        /**
         * Initializes storages.
         *
         * @returns {Object} Chainable.
         */
        initStorages: function () {
            _.each(this.storagesNamespace, function (name) {
                this[name] = storage.createStorage(this.storagesConfiguration[name]);

                if (this.storagesConfiguration[name].savePrevious) {
                    this[name].previous = storage.createStorage(this.storagesConfiguration[name].savePrevious);
                }
            }.bind(this));

            return this;
        },

        /**
         * Initializes start data.
         *
         * @returns {Object} Chainable.
         */
        initStartData: function () {
            _.each(this.storagesNamespace, function (name) {
                this.updateDataHandler(name, this[name].get());
            }.bind(this));

            return this;
        },

        /**
         * Prepare storages congfig.
         *
         * @returns {Object} Chainable.
         */
        prepareStoragesConfig: function () {
            this.storagesNamespace = _.keys(this.storagesConfiguration);

            _.each(this.storagesNamespace, function (name) {
                this.storagesConfiguration[name].requestConfig = _.extend(
                    utils.copy(this.requestConfig),
                    this.storagesConfiguration[name].requestConfig
                );
            }.bind(this));

            return this;
        },

        /**
         * Prepare date in UTC format (in GMT), and calculate unix timestamp based in seconds
         *
         * @returns {Number}
         * @private
         */
        getUtcTime: function () {
            return new Date().getTime() / 1000;
        },

        /**
         * Initializes listeners to storages "data" property.
         */
        initUpdateStorageDataListener: function () {
            _.each(this.storagesNamespace, function (name) {
                if (this[name].data) {
                    this[name].data.subscribe(this.updateDataHandler.bind(this, name));
                }
            }.bind(this));
        },

        /**
         * Handlers for storages "data" property
         */
        updateDataHandler: function (name, data) {
            var previousData = this[name].previous ? this[name].previous.get() : false;

            if (!_.isEmpty(previousData) &&
                !_.isEmpty(data) &&
                !utils.compare(data, previousData).equal) {
                this[name].set(data);
                this[name].previous.set(data);
                this.sendRequest(name, data);
            } else if (
                _.isEmpty(previousData) &&
                !_.isEmpty(data)
            ) {
                this[name].set(data);
                this.sendRequest(name, data);
            }
        },

        /**
         * Gets last updated time
         *
         * @param {String} name - storage name
         */
        getLastUpdate: function (name) {
            return window.localStorage.getItem(this[name].namespace + '_last_update');
        },

        /**
         * Sets last updated time
         *
         * @param {String} name - storage name
         */
        setLastUpdate: function (name) {
            window.localStorage.setItem(this[name].namespace + '_last_update', this.getUtcTime());
        },

        /**
         * Request handler
         *
         * @param {String} name - storage name
         */
        requestHandler: function (name) {
            this.setLastUpdate(name);
            this.requestSent = 1;
        },

        /**
         * Sends request to server to gets data
         *
         * @param {String} name - storage name
         * @param {Object} data - ids
         */
        sendRequest: function (name, data) {
            var params  = utils.copy(this.storagesConfiguration[name].requestConfig),
                url = params.syncUrl,
                typeId = params.typeId;

            if (this.requestSent || !~~this.storagesConfiguration[name].allowToSendRequest) {
                return;
            }

            delete params.typeId;
            delete params.url;
            this.requestSent = 1;

            return utils.ajaxSubmit({
                url: url,
                data: {
                    ids: data,
                    'type_id': typeId
                }
            }, params).done(this.requestHandler.bind(this, name));
        }
    });
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Theme/js/view/messages',[
    'jquery',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'underscore',
    'escaper',
    'jquery/jquery-storageapi'
], function ($, Component, customerData, _, escaper) {
    'use strict';

    return Component.extend({
        defaults: {
            cookieMessages: [],
            cookieMessagesObservable: [],
            messages: [],
            allowedTags: ['div', 'span', 'b', 'strong', 'i', 'em', 'u', 'a']
        },

        /**
         * Extends Component object by storage observable messages.
         */
        initialize: function () {
            this._super().observe(
                [
                    'cookieMessagesObservable'
                ]
            );

            // The "cookieMessages" variable is not used anymore. It exists for backward compatibility; to support
            // merchants who have overwritten "messages.phtml" which would still point to cookieMessages instead of the
            // observable variant (also see https://github.com/magento/magento2/pull/37309).
            this.cookieMessages = _.unique($.cookieStorage.get('mage-messages'), 'text');
            this.cookieMessagesObservable(this.cookieMessages);

            this.messages = customerData.get('messages').extend({
                disposableCustomerData: 'messages'
            });

            $.mage.cookies.set('mage-messages', '', {
                samesite: 'strict',
                domain: ''
            });
        },

        /**
         * Prepare the given message to be rendered as HTML
         *
         * @param {String} message
         * @return {String}
         */
        prepareMessageForHtml: function (message) {
            return escaper.escapeHtml(message, this.allowedTags);
        },
        purgeMessages: function () {
            if (!_.isEmpty(this.messages().messages)) {
                customerData.set('messages', {});
            }
        }
    });
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

/**
 * Cloned vendor/magento/module-catalog/view/base/web/js/price-utils.js as in case of mixing there is still need to copy
 * 85% of file. And rest 15% is barely not used.
 * Need to clone appears to prevent corruption of globalPriceFormat with options passed inside each formatPrice call.
 *
 * @api
 */
define('Magento_Catalog/js/price-utils',['jquery', 'price-format-config'], function ($, priceFormatConfig) {
    'use strict';

    var globalPriceFormat = {
        requiredPrecision: 2,
        precision: 2,
        integerRequired: 1,
        decimalSymbol: ',',
        groupSymbol: ' ',
        groupLength: 3
    };

    /**
     * Repeats {string} {times} times
     * @param  {String} string
     * @param  {Number} times
     * @return {String}
     */
    function stringPad(string, times) {
        return (new Array(times + 1)).join(string);
    }

    /**
     * Format the price with the compliance to the specified locale
     *
     * @param {Number} amount
     * @param {Object} format
     * @param  {Boolean} isShowSign
     */
    function formatPriceLocale(amount, format, isShowSign)
    {
        var s = '',
            precision, pattern, locale, r;

        format = _.extend(globalPriceFormat, format);
        precision = isNaN(format.requiredPrecision = Math.abs(format.requiredPrecision)) ? 2 : format.requiredPrecision;
        pattern = format.pattern || '%s';
        locale = window.LOCALE || 'en-US';
        if (isShowSign === undefined || isShowSign === true) {
            s = amount < 0 ? '-' : isShowSign ? '+' : '';
        } else if (isShowSign === false) {
            s = '';
        }
        pattern = pattern.indexOf('{sign}') < 0 ? s + pattern : pattern.replace('{sign}', s);
        amount = Number(Math.round(Math.abs(+amount || 0) + 'e+' + precision) + ('e-' + precision));
        r = amount.toLocaleString(locale, {minimumFractionDigits: precision});

        return pattern.replace('%s', r).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    }

    /**
     * Formatter for price amount
     * @param  {Number}  amount
     * @param  {Object}  format
     * @param  {Boolean} isShowSign
     * @return {String}              Formatted value
     */
    function formatPrice(amount, format, isShowSign) {
        var s = '',
            precision, integerRequired, decimalSymbol, groupSymbol, groupLength, pattern, i, pad, j, re, r, am;

        format = $.extend(true, {}, $.extend(true, {}, globalPriceFormat), $.extend(true, {}, priceFormatConfig), format || {});

        // copied from price-option.js | Could be refactored with varien/js.js

        precision = isNaN(format.requiredPrecision = Math.abs(format.requiredPrecision)) ? 2 : format.requiredPrecision;
        integerRequired = isNaN(format.integerRequired = Math.abs(format.integerRequired)) ? 1 : format.integerRequired;
        decimalSymbol = format.decimalSymbol === undefined ? ',' : format.decimalSymbol;
        groupSymbol = format.groupSymbol === undefined ? '.' : format.groupSymbol;
        groupLength = format.groupLength === undefined ? 3 : format.groupLength;
        pattern = format.pattern || '%s';

        if (isShowSign === undefined || isShowSign === true) {
            s = amount < 0 ? '-' : isShowSign ? '+' : '';
        } else if (isShowSign === false) {
            s = '';
        }
        pattern = pattern.indexOf('{sign}') < 0 ? s + pattern : pattern.replace('{sign}', s);

        // we're avoiding the usage of to fixed, and using round instead with the e representation to address
        // numbers like 1.005 = 1.01. Using ToFixed to only provide trailig zeroes in case we have a whole number
        i = parseInt(
                amount = Number(Math.round(Math.abs(+amount || 0) + 'e+' + precision) + ('e-' + precision)),
                10
            ) + '';
        pad = i.length < integerRequired ? integerRequired - i.length : 0;

        i = stringPad('0', pad) + i;

        j = i.length > groupLength ? i.length % groupLength : 0;
        re = new RegExp('(\\d{' + groupLength + '})(?=\\d)', 'g');

        // replace(/-/, 0) is only for fixing Safari bug which appears
        // when Math.abs(0).toFixed() executed on '0' number.
        // Result is '0.-0' :(

        am = Number(Math.round(Math.abs(amount - i) + 'e+' + precision) + ('e-' + precision));
        r = (j ? i.substr(0, j) + groupSymbol : '') +
            i.substr(j).replace(re, '$1' + groupSymbol) +
            (precision ? decimalSymbol + am.toFixed(precision).replace(/-/, 0).slice(2) : '');

        return pattern.replace('%s', r).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    }

    /**
     * Deep clone of Object. Doesn't support functions
     * @param {Object} obj
     * @return {Object}
     */
    function objectDeepClone(obj) {
        return JSON.parse(JSON.stringify(obj));
    }

    /**
     * Helper to find ID in name attribute
     * @param   {jQuery} element
     * @returns {undefined|String}
     */
    function findOptionId(element) {
        var re, id, name;

        if (!element) {
            return id;
        }
        name = $(element).attr('name');

        if (name.indexOf('[') !== -1) {
            re = /\[([^\]]+)?\]/;
        } else {
            re = /_([^\]]+)?_/; // just to support file-type-option
        }
        id = re.exec(name) && re.exec(name)[1];

        if (id) {
            return id;
        }
    }

    return {
        formatPriceLocale: formatPriceLocale,
        formatPrice: formatPrice,
        deepClone: objectDeepClone,
        strPad: stringPad,
        findOptionId: findOptionId
    };
});

/**
 * Copyright © Vaimo Group. All rights reserved.
 * See LICENSE_VAIMO.txt for license details.
 */

define('Magento_Checkout/js/view/minicart',[
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'jquery',
    'ko',
    'underscore',
    'Magento_Catalog/js/price-utils',
    'mage/translate',
    'vaimo/overlay',
    'postcodeUtils',
    'uiRegistry',
    'vaimo/headerState',
    'mage/storage',
    'jquery/jquery-storageapi'
], function (Component, customerData, $, ko, _, priceUtils, $t, overlay, postcodeUtils, registry, headerState) {
'use strict';

return Component.extend({
shoppingCartUrl: window.checkout.shoppingCartUrl,
maxItemsToDisplay: window.checkout.maxItemsToDisplay,
cart: {},
isLoading: ko.observable(false),
addToCartCalls: 0,
minicartSelector: '[data-block="minicart"]',
sidebarOptions: {
    'targetElement': '',
    'url': {
        'checkout': window.checkout.checkoutUrl,
        'update': window.checkout.updateItemQtyUrl,
        'remove': window.checkout.removeItemUrl,
        'loginUrl': window.checkout.customerLoginUrl,
        'isRedirectRequired': window.checkout.isRedirectRequired
    },
    'button': {
        'checkout': '[data-minicart-goto-checkout]',
        'remove': '[data-cart-item-remove]'
    },
    'showcart': {
        'parent': 'span.counter',
        'qty': 'span.counter-number',
        'label': 'span.counter-label'
    },
    'minicart': {
        'list': '#mini-cart',
        'content': '#minicart-content-wrapper',
        'qty': 'div.items-total',
        'subtotal': 'div.subtotal span.price',
        'maxItemsVisible': window.checkout.minicartMaxItemsVisible
    },
    'item': {
        'qty': ':input.cart-item-qty',
        'qtyText': '[data-minicart-qty-text]',
        'button': ':button.update-cart-item'
    },
    'confirmMessage': $t('Are you sure you would like to remove this item from the shopping cart?'),
    'productItemSelector': ''
},
autoCloseTimeout: 3000,
dropDownDialogSelector: '[data-role="dropdownDialog"]',
productItemSelector: '[data-role=product-item]',
deliveryMessageText: $t('You have chosen home delivery.'),
afterAddToCart: 'minicart--after-add-to-cart',
productSku: 'data-product-sku',
availableItemIdPrefix: '#available-accessories-item-',

initialize: function () {
    this.$filteringProductNameEl = $('<div/>');
    this.filteringProductNameRegexp = /([\/.,\-:;!?|])/gmi;
    this.$miniCart = $(this.minicartSelector);
    this.$dropDownDialog = this.$miniCart.find(this.dropDownDialogSelector);

    this._initCartParam('minicart_is_defined')(false);
    this._initCartParam('summary_count').subscribe((function (value) {
        this._initCartParam('minicart_is_defined')(true);
        if (value <= 0) {
            this.closeMinicart();
        }
    }).bind(this));


    this.$miniCart
        .on('dropdowndialogopen', (function () {
            this.initSidebar();
            this.$miniCart.removeClass(this.afterAddToCart);
            registry.set('initSatellitePostcode', true);
        }).bind(this))
        .on('dropdowndialogclose', (function () {
            clearTimeout(this.closeCartAfterFetchTimeout);
        }).bind(this))
        .on('contentLoading', (function () {
            this.addToCartCalls++;
            this.isLoading(true);
        }).bind(this));

    customerData.getInitCustomerData().done(function () {
        var cartData = customerData.get('cart');

        this.update(cartData());
        cartData.subscribe(function(updatedCart) {
            this.addToCartCalls--;
            this.isLoading(this.addToCartCalls > 0);
            this.update(updatedCart);
            this.initSidebar();

            $(document).on('sidebarFailedAjax', this.closeMinicart.bind(this));
        }.bind(this));

        if (cartData()['website_id'] !== window.checkout.websiteId) {
            customerData.reload(['cart'], false, false);
        }
    }.bind(this));

    this.isLoading.subscribe(function(response) {
        if (response) {
            overlay.open();
            headerState.setState('isMinicartLoading', true);
        } else {
            overlay.close();
            headerState.setState('isMinicartLoading', false);
            this.$dropDownDialog.simpleDropdown('open', {noHeaderStateTrigger: true});
            this.$miniCart.addClass(this.afterAddToCart);
            $('body').removeClass('no-scroll');
            clearTimeout(this.closeCartAfterFetchTimeout);
            this.closeCartAfterFetchTimeout = window.setTimeout((function () {
                this.$dropDownDialog.simpleDropdown('close');
            }).bind(this), this.autoCloseTimeout);
        }

    }.bind(this));

    this.observe({deliveryMessage: this.deliveryMessageText, postcode: undefined});
    return this._super();
},

initSidebar: function () {
    require(['sidebar'], (function(){
        if (this.$miniCart.data('mageSidebar')) {
            this.$miniCart.sidebar('update');
        }

        if (!$(this.productItemSelector).length) {
            return;
        }

        this.$miniCart.trigger('contentUpdated');

        this.$miniCart.sidebar($.extend(true, {
            targetElement: this.$dropDownDialog,
            productItemSelector: this.productItemSelector
        }, this.sidebarOptions));
        registry.set('minicartComponent', this.$miniCart);
    }).bind(this));
},

closeMinicart: function () {
    this.$dropDownDialog.simpleDropdown('close');
},

getItemRenderer: function (productType) {
    return this.itemRenderer[productType] || 'defaultRenderer';
},

update: function (updatedCart) {
    _.each(updatedCart, function (value, key) {
        if (!this.cart.hasOwnProperty(key)) {
            this.cart[key] = ko.observable();
        }
        this.cart[key](value);
    }, this);
},

getCartParam: function (name) {
    this._initCartParam(name);
    return this.cart[name]();
},

_initCartParam: function (name) {
    if (!_.isUndefined(name)) {
        if (!this.cart.hasOwnProperty(name)) {
            this.cart[name] = ko.observable();
        }
    }

    return this.cart[name];
},

getCartItems: function () {
    var result = this.getUpdatedItem();

    for (var i = 0; i < result.length; i++) {
        result[i]['display_product_name'] = (this.$filteringProductNameEl.html(result[i]['product_name']).text() || '')
            .replace(this.filteringProductNameRegexp, '$1<wbr/>');
    }
    return result;
},

getUpdatedItem: function () {
    var result = (this.getCartParam('items') || []).slice(parseInt(-this.maxItemsToDisplay, 10));

    for (var i = 0; i < result.length; i++) {
        const incitoResult = $('.active[data-id=' + result[i]['product_sku'] + ']').attr('data-id');

        if (result[i]['product_sku'] === $(this.availableItemIdPrefix + result[i]['product_id']).attr(this.productSku)) {
            result[i]['is_updated'] = 'updated';
            break
        } else if (result[i]['product_sku'] === $('.product-sku-' + result[i]['product_sku']).attr(this.productSku)) {
            result[i]['is_updated'] = 'updated';
            break
        } else if (typeof incitoResult !== 'undefined' && result[i]['product_sku'] === incitoResult) {
            result[i]['is_updated'] = 'updated';
        }
    }
    return result;
},

getCartLineItemsCount: function () {
    var items = this.getCartParam('items') || [];

    return parseInt(items.length, 10);
},

formatPrice: function (amount) {
    return priceUtils.formatPrice(amount);
},

getCartCount: function () {
    const count = parseInt(this.getCartParam('summary_count'));
    return count > 99 ? '99+' : count;
},

getLabelTotalPrice: function () {
    if (this.getCartParam('isB2b')) {
        return this.getCartParam('grand_total_excl_tax')
    }

    return this.getCartParam('grand_total_incl_tax');
},

isCartEmpty: function() {
    const hasItems = this.getCartCount() === 0;
    return this.getCartParam('minicart_is_defined') && hasItems
},

isCartFull: function() {
    const hasItems = this.getCartCount() !== 0;
    return this.getCartParam('minicart_is_defined') && hasItems
},

getFormattedPostcode: function() {
    const postcode = this.postcode();
    return postcode ? postcodeUtils.format(postcode) : '';
}
});
});