/*!
 * jQuery JavaScript Library v3.6.0
 * https://jquery.com/
 *
 * Includes Sizzle.js
 * https://sizzlejs.com/
 *
 * Copyright OpenJS Foundation and other contributors
 * Released under the MIT license
 * https://jquery.org/license
 *
 * Date: 2021-03-02T17:08Z
 */
( function( global, factory ) {

    "use strict";

    if ( typeof module === "object" && typeof module.exports === "object" ) {

        // For CommonJS and CommonJS-like environments where a proper `window`
        // is present, execute the factory and get jQuery.
        // For environments that do not have a `window` with a `document`
        // (such as Node.js), expose a factory as module.exports.
        // This accentuates the need for the creation of a real `window`.
        // e.g. var jQuery = require("jquery")(window);
        // See ticket #14549 for more info.
        module.exports = global.document ?
            factory( global, true ) :
            function( w ) {
                if ( !w.document ) {
                    throw new Error( "jQuery requires a window with a document" );
                }
                return factory( w );
            };
    } else {
        factory( global );
    }

// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
// enough that all such attempts are guarded in a try block.
    "use strict";

    var arr = [];

    var getProto = Object.getPrototypeOf;

    var slice = arr.slice;

    var flat = arr.flat ? function( array ) {
        return arr.flat.call( array );
    } : function( array ) {
        return arr.concat.apply( [], array );
    };


    var push = arr.push;

    var indexOf = arr.indexOf;

    var class2type = {};

    var toString = class2type.toString;

    var hasOwn = class2type.hasOwnProperty;

    var fnToString = hasOwn.toString;

    var ObjectFunctionString = fnToString.call( Object );

    var support = {};

    var isFunction = function isFunction( obj ) {

        // Support: Chrome <=57, Firefox <=52
        // In some browsers, typeof returns "function" for HTML <object> elements
        // (i.e., `typeof document.createElement( "object" ) === "function"`).
        // We don't want to classify *any* DOM node as a function.
        // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
        // Plus for old WebKit, typeof returns "function" for HTML collections
        // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
        return typeof obj === "function" && typeof obj.nodeType !== "number" &&
            typeof obj.item !== "function";
    };


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


    var document = window.document;



    var preservedScriptAttributes = {
        type: true,
        src: true,
        nonce: true,
        noModule: true
    };

    function DOMEval( code, node, doc ) {
        doc = doc || document;

        var i, val,
            script = doc.createElement( "script" );

        script.text = code;
        if ( node ) {
            for ( i in preservedScriptAttributes ) {

                // Support: Firefox 64+, Edge 18+
                // Some browsers don't support the "nonce" property on scripts.
                // On the other hand, just using `getAttribute` is not enough as
                // the `nonce` attribute is reset to an empty string whenever it
                // becomes browsing-context connected.
                // See https://github.com/whatwg/html/issues/2369
                // See https://html.spec.whatwg.org/#nonce-attributes
                // The `node.getAttribute` check was added for the sake of
                // `jQuery.globalEval` so that it can fake a nonce-containing node
                // via an object.
                val = node[ i ] || node.getAttribute && node.getAttribute( i );
                if ( val ) {
                    script.setAttribute( i, val );
                }
            }
        }
        doc.head.appendChild( script ).parentNode.removeChild( script );
    }


    function toType( obj ) {
        if ( obj == null ) {
            return obj + "";
        }

        // Support: Android <=2.3 only (functionish RegExp)
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[ toString.call( obj ) ] || "object" :
            typeof obj;
    }
    /* global Symbol */
// Defining this global in .eslintrc.json would create a danger of using the global
// unguarded in another place, it seems safer to define global only for this module



    var
        version = "3.6.0",

        // Define a local copy of jQuery
        jQuery = function( selector, context ) {

            // The jQuery object is actually just the init constructor 'enhanced'
            // Need init if jQuery is called (just allow error to be thrown if not included)
            return new jQuery.fn.init( selector, context );
        };

    jQuery.fn = jQuery.prototype = {

        // The current version of jQuery being used
        jquery: version,

        constructor: jQuery,

        // The default length of a jQuery object is 0
        length: 0,

        toArray: function() {
            return slice.call( this );
        },

        // Get the Nth element in the matched element set OR
        // Get the whole matched element set as a clean array
        get: function( num ) {

            // Return all the elements in a clean array
            if ( num == null ) {
                return slice.call( this );
            }

            // Return just the one element from the set
            return num < 0 ? this[ num + this.length ] : this[ num ];
        },

        // Take an array of elements and push it onto the stack
        // (returning the new matched element set)
        pushStack: function( elems ) {

            // Build a new jQuery matched element set
            var ret = jQuery.merge( this.constructor(), elems );

            // Add the old object onto the stack (as a reference)
            ret.prevObject = this;

            // Return the newly-formed element set
            return ret;
        },

        // Execute a callback for every element in the matched set.
        each: function( callback ) {
            return jQuery.each( this, callback );
        },

        map: function( callback ) {
            return this.pushStack( jQuery.map( this, function( elem, i ) {
                return callback.call( elem, i, elem );
            } ) );
        },

        slice: function() {
            return this.pushStack( slice.apply( this, arguments ) );
        },

        first: function() {
            return this.eq( 0 );
        },

        last: function() {
            return this.eq( -1 );
        },

        even: function() {
            return this.pushStack( jQuery.grep( this, function( _elem, i ) {
                return ( i + 1 ) % 2;
            } ) );
        },

        odd: function() {
            return this.pushStack( jQuery.grep( this, function( _elem, i ) {
                return i % 2;
            } ) );
        },

        eq: function( i ) {
            var len = this.length,
                j = +i + ( i < 0 ? len : 0 );
            return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
        },

        end: function() {
            return this.prevObject || this.constructor();
        },

        // For internal use only.
        // Behaves like an Array's method, not like a jQuery method.
        push: push,
        sort: arr.sort,
        splice: arr.splice
    };

    jQuery.extend = jQuery.fn.extend = function() {
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[ 0 ] || {},
            i = 1,
            length = arguments.length,
            deep = false;

        // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
            deep = target;

            // Skip the boolean and the target
            target = arguments[ i ] || {};
            i++;
        }

        // Handle case when target is a string or something (possible in deep copy)
        if ( typeof target !== "object" && !isFunction( target ) ) {
            target = {};
        }

        // Extend jQuery itself if only one argument is passed
        if ( i === length ) {
            target = this;
            i--;
        }

        for ( ; i < length; i++ ) {

            // Only deal with non-null/undefined values
            if ( ( options = arguments[ i ] ) != null ) {

                // Extend the base object
                for ( name in options ) {
                    copy = options[ name ];

                    // Prevent Object.prototype pollution
                    // Prevent never-ending loop
                    if ( name === "__proto__" || target === copy ) {
                        continue;
                    }

                    // Recurse if we're merging plain objects or arrays
                    if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                        ( copyIsArray = Array.isArray( copy ) ) ) ) {
                        src = target[ name ];

                        // Ensure proper type for the source value
                        if ( copyIsArray && !Array.isArray( src ) ) {
                            clone = [];
                        } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
                            clone = {};
                        } else {
                            clone = src;
                        }
                        copyIsArray = false;

                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );

                        // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }

        // Return the modified object
        return target;
    };

    jQuery.extend( {

        // Unique for each copy of jQuery on the page
        expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),

        // Assume jQuery is ready without the ready module
        isReady: true,

        error: function( msg ) {
            throw new Error( msg );
        },

        noop: function() {},

        isPlainObject: function( 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;
        },

        isEmptyObject: function( obj ) {
            var name;

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

        // Evaluates a script in a provided context; falls back to the global one
        // if not specified.
        globalEval: function( code, options, doc ) {
            DOMEval( code, { nonce: options && options.nonce }, doc );
        },

        each: function( obj, callback ) {
            var length, i = 0;

            if ( isArrayLike( obj ) ) {
                length = obj.length;
                for ( ; i < length; i++ ) {
                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                        break;
                    }
                }
            }

            return obj;
        },

        // results is for internal usage only
        makeArray: function( arr, results ) {
            var ret = results || [];

            if ( arr != null ) {
                if ( isArrayLike( Object( arr ) ) ) {
                    jQuery.merge( ret,
                        typeof arr === "string" ?
                            [ arr ] : arr
                    );
                } else {
                    push.call( ret, arr );
                }
            }

            return ret;
        },

        inArray: function( elem, arr, i ) {
            return arr == null ? -1 : indexOf.call( arr, elem, i );
        },

        // Support: Android <=4.0 only, PhantomJS 1 only
        // push.apply(_, arraylike) throws on ancient WebKit
        merge: function( first, second ) {
            var len = +second.length,
                j = 0,
                i = first.length;

            for ( ; j < len; j++ ) {
                first[ i++ ] = second[ j ];
            }

            first.length = i;

            return first;
        },

        grep: function( elems, callback, invert ) {
            var callbackInverse,
                matches = [],
                i = 0,
                length = elems.length,
                callbackExpect = !invert;

            // Go through the array, only saving the items
            // that pass the validator function
            for ( ; i < length; i++ ) {
                callbackInverse = !callback( elems[ i ], i );
                if ( callbackInverse !== callbackExpect ) {
                    matches.push( elems[ i ] );
                }
            }

            return matches;
        },

        // arg is for internal usage only
        map: function( elems, callback, arg ) {
            var length, value,
                i = 0,
                ret = [];

            // Go through the array, translating each of the items to their new values
            if ( isArrayLike( elems ) ) {
                length = elems.length;
                for ( ; i < length; i++ ) {
                    value = callback( elems[ i ], i, arg );

                    if ( value != null ) {
                        ret.push( value );
                    }
                }

                // Go through every key on the object,
            } else {
                for ( i in elems ) {
                    value = callback( elems[ i ], i, arg );

                    if ( value != null ) {
                        ret.push( value );
                    }
                }
            }

            // Flatten any nested arrays
            return flat( ret );
        },

        // A global GUID counter for objects
        guid: 1,

        // jQuery.support is not used in Core but other projects attach their
        // properties to it so it needs to exist.
        support: support
    } );

    if ( typeof Symbol === "function" ) {
        jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
    }

// Populate the class2type map
    jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
        function( _i, name ) {
            class2type[ "[object " + name + "]" ] = name.toLowerCase();
        } );

    function isArrayLike( obj ) {

        // Support: real iOS 8.2 only (not reproducible in simulator)
        // `in` check used to prevent JIT error (gh-2145)
        // hasOwn isn't used here due to false negatives
        // regarding Nodelist length in IE
        var length = !!obj && "length" in obj && obj.length,
            type = toType( obj );

        if ( isFunction( obj ) || isWindow( obj ) ) {
            return false;
        }

        return type === "array" || length === 0 ||
            typeof length === "number" && length > 0 && ( length - 1 ) in obj;
    }
    var Sizzle =
        /*!
 * Sizzle CSS Selector Engine v2.3.6
 * https://sizzlejs.com/
 *
 * Copyright JS Foundation and other contributors
 * Released under the MIT license
 * https://js.foundation/
 *
 * Date: 2021-02-16
 */
        ( function( window ) {
            var i,
                support,
                Expr,
                getText,
                isXML,
                tokenize,
                compile,
                select,
                outermostContext,
                sortInput,
                hasDuplicate,

                // Local document vars
                setDocument,
                document,
                docElem,
                documentIsHTML,
                rbuggyQSA,
                rbuggyMatches,
                matches,
                contains,

                // Instance-specific data
                expando = "sizzle" + 1 * new Date(),
                preferredDoc = window.document,
                dirruns = 0,
                done = 0,
                classCache = createCache(),
                tokenCache = createCache(),
                compilerCache = createCache(),
                nonnativeSelectorCache = createCache(),
                sortOrder = function( a, b ) {
                    if ( a === b ) {
                        hasDuplicate = true;
                    }
                    return 0;
                },

                // Instance methods
                hasOwn = ( {} ).hasOwnProperty,
                arr = [],
                pop = arr.pop,
                pushNative = arr.push,
                push = arr.push,
                slice = arr.slice,

                // Use a stripped-down indexOf as it's faster than native
                // https://jsperf.com/thor-indexof-vs-for/5
                indexOf = function( list, elem ) {
                    var i = 0,
                        len = list.length;
                    for ( ; i < len; i++ ) {
                        if ( list[ i ] === elem ) {
                            return i;
                        }
                    }
                    return -1;
                },

                booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
                    "ismap|loop|multiple|open|readonly|required|scoped",

                // Regular expressions

                // http://www.w3.org/TR/css3-selectors/#whitespace
                whitespace = "[\\x20\\t\\r\\n\\f]",

                // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
                identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
                    "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",

                // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
                attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +

                    // Operator (capture 2)
                    "*([*^$|!~]?=)" + whitespace +

                    // "Attribute values must be CSS identifiers [capture 5]
                    // or strings [capture 3 or capture 4]"
                    "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
                    whitespace + "*\\]",

                pseudos = ":(" + identifier + ")(?:\\((" +

                    // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
                    // 1. quoted (capture 3; capture 4 or capture 5)
                    "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +

                    // 2. simple (capture 6)
                    "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +

                    // 3. anything else (capture 2)
                    ".*" +
                    ")\\)|)",

                // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
                rwhitespace = new RegExp( whitespace + "+", "g" ),
                rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
                    whitespace + "+$", "g" ),

                rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
                rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
                    "*" ),
                rdescend = new RegExp( whitespace + "|>" ),

                rpseudo = new RegExp( pseudos ),
                ridentifier = new RegExp( "^" + identifier + "$" ),

                matchExpr = {
                    "ID": new RegExp( "^#(" + identifier + ")" ),
                    "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
                    "TAG": new RegExp( "^(" + identifier + "|[*])" ),
                    "ATTR": new RegExp( "^" + attributes ),
                    "PSEUDO": new RegExp( "^" + pseudos ),
                    "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
                        whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
                        whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
                    "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),

                    // For use in libraries implementing .is()
                    // We use this for POS matching in `select`
                    "needsContext": new RegExp( "^" + whitespace +
                        "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
                        "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
                },

                rhtml = /HTML$/i,
                rinputs = /^(?:input|select|textarea|button)$/i,
                rheader = /^h\d$/i,

                rnative = /^[^{]+\{\s*\[native \w/,

                // Easily-parseable/retrievable ID or TAG or CLASS selectors
                rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

                rsibling = /[+~]/,

                // CSS escapes
                // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
                runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
                funescape = function( escape, nonHex ) {
                    var high = "0x" + escape.slice( 1 ) - 0x10000;

                    return nonHex ?

                        // Strip the backslash prefix from a non-hex escape sequence
                        nonHex :

                        // Replace a hexadecimal escape sequence with the encoded Unicode code point
                        // Support: IE <=11+
                        // For values outside the Basic Multilingual Plane (BMP), manually construct a
                        // surrogate pair
                        high < 0 ?
                            String.fromCharCode( high + 0x10000 ) :
                            String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
                },

                // CSS string/identifier serialization
                // https://drafts.csswg.org/cssom/#common-serializing-idioms
                rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
                fcssescape = function( ch, asCodePoint ) {
                    if ( asCodePoint ) {

                        // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
                        if ( ch === "\0" ) {
                            return "\uFFFD";
                        }

                        // Control characters and (dependent upon position) numbers get escaped as code points
                        return ch.slice( 0, -1 ) + "\\" +
                            ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
                    }

                    // Other potentially-special ASCII characters get backslash-escaped
                    return "\\" + ch;
                },

                // Used for iframes
                // See setDocument()
                // Removing the function wrapper causes a "Permission Denied"
                // error in IE
                unloadHandler = function() {
                    setDocument();
                },

                inDisabledFieldset = addCombinator(
                    function( elem ) {
                        return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
                    },
                    { dir: "parentNode", next: "legend" }
                );

// Optimize for push.apply( _, NodeList )
            try {
                push.apply(
                    ( arr = slice.call( preferredDoc.childNodes ) ),
                    preferredDoc.childNodes
                );

                // Support: Android<4.0
                // Detect silently failing push.apply
                // eslint-disable-next-line no-unused-expressions
                arr[ preferredDoc.childNodes.length ].nodeType;
            } catch ( e ) {
                push = { apply: arr.length ?

                        // Leverage slice if possible
                        function( target, els ) {
                            pushNative.apply( target, slice.call( els ) );
                        } :

                        // Support: IE<9
                        // Otherwise append directly
                        function( target, els ) {
                            var j = target.length,
                                i = 0;

                            // Can't trust NodeList.length
                            while ( ( target[ j++ ] = els[ i++ ] ) ) {}
                            target.length = j - 1;
                        }
                };
            }

            function Sizzle( selector, context, results, seed ) {
                var m, i, elem, nid, match, groups, newSelector,
                    newContext = context && context.ownerDocument,

                    // nodeType defaults to 9, since context defaults to document
                    nodeType = context ? context.nodeType : 9;

                results = results || [];

                // Return early from calls with invalid selector or context
                if ( typeof selector !== "string" || !selector ||
                    nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {

                    return results;
                }

                // Try to shortcut find operations (as opposed to filters) in HTML documents
                if ( !seed ) {
                    setDocument( context );
                    context = context || document;

                    if ( documentIsHTML ) {

                        // If the selector is sufficiently simple, try using a "get*By*" DOM method
                        // (excepting DocumentFragment context, where the methods don't exist)
                        if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {

                            // ID selector
                            if ( ( m = match[ 1 ] ) ) {

                                // Document context
                                if ( nodeType === 9 ) {
                                    if ( ( elem = context.getElementById( m ) ) ) {

                                        // Support: IE, Opera, Webkit
                                        // TODO: identify versions
                                        // getElementById can match elements by name instead of ID
                                        if ( elem.id === m ) {
                                            results.push( elem );
                                            return results;
                                        }
                                    } else {
                                        return results;
                                    }

                                    // Element context
                                } else {

                                    // Support: IE, Opera, Webkit
                                    // TODO: identify versions
                                    // getElementById can match elements by name instead of ID
                                    if ( newContext && ( elem = newContext.getElementById( m ) ) &&
                                        contains( context, elem ) &&
                                        elem.id === m ) {

                                        results.push( elem );
                                        return results;
                                    }
                                }

                                // Type selector
                            } else if ( match[ 2 ] ) {
                                push.apply( results, context.getElementsByTagName( selector ) );
                                return results;

                                // Class selector
                            } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
                                context.getElementsByClassName ) {

                                push.apply( results, context.getElementsByClassName( m ) );
                                return results;
                            }
                        }

                        // Take advantage of querySelectorAll
                        if ( support.qsa &&
                            !nonnativeSelectorCache[ selector + " " ] &&
                            ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&

                            // Support: IE 8 only
                            // Exclude object elements
                            ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {

                            newSelector = selector;
                            newContext = context;

                            // qSA considers elements outside a scoping root when evaluating child or
                            // descendant combinators, which is not what we want.
                            // In such cases, we work around the behavior by prefixing every selector in the
                            // list with an ID selector referencing the scope context.
                            // The technique has to be used as well when a leading combinator is used
                            // as such selectors are not recognized by querySelectorAll.
                            // Thanks to Andrew Dupont for this technique.
                            if ( nodeType === 1 &&
                                ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {

                                // Expand context for sibling selectors
                                newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
                                    context;

                                // We can use :scope instead of the ID hack if the browser
                                // supports it & if we're not changing the context.
                                if ( newContext !== context || !support.scope ) {

                                    // Capture the context ID, setting it first if necessary
                                    if ( ( nid = context.getAttribute( "id" ) ) ) {
                                        nid = nid.replace( rcssescape, fcssescape );
                                    } else {
                                        context.setAttribute( "id", ( nid = expando ) );
                                    }
                                }

                                // Prefix every selector in the list
                                groups = tokenize( selector );
                                i = groups.length;
                                while ( i-- ) {
                                    groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
                                        toSelector( groups[ i ] );
                                }
                                newSelector = groups.join( "," );
                            }

                            try {
                                push.apply( results,
                                    newContext.querySelectorAll( newSelector )
                                );
                                return results;
                            } catch ( qsaError ) {
                                nonnativeSelectorCache( selector, true );
                            } finally {
                                if ( nid === expando ) {
                                    context.removeAttribute( "id" );
                                }
                            }
                        }
                    }
                }

                // All others
                return select( selector.replace( rtrim, "$1" ), context, results, seed );
            }

            /**
             * Create key-value caches of limited size
             * @returns {function(string, object)} Returns the Object data after storing it on itself with
             *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
             *	deleting the oldest entry
             */
            function createCache() {
                var keys = [];

                function cache( key, value ) {

                    // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
                    if ( keys.push( key + " " ) > Expr.cacheLength ) {

                        // Only keep the most recent entries
                        delete cache[ keys.shift() ];
                    }
                    return ( cache[ key + " " ] = value );
                }
                return cache;
            }

            /**
             * Mark a function for special use by Sizzle
             * @param {Function} fn The function to mark
             */
            function markFunction( fn ) {
                fn[ expando ] = true;
                return fn;
            }

            /**
             * Support testing using an element
             * @param {Function} fn Passed the created element and returns a boolean result
             */
            function assert( fn ) {
                var el = document.createElement( "fieldset" );

                try {
                    return !!fn( el );
                } catch ( e ) {
                    return false;
                } finally {

                    // Remove from its parent by default
                    if ( el.parentNode ) {
                        el.parentNode.removeChild( el );
                    }

                    // release memory in IE
                    el = null;
                }
            }

            /**
             * Adds the same handler for all of the specified attrs
             * @param {String} attrs Pipe-separated list of attributes
             * @param {Function} handler The method that will be applied
             */
            function addHandle( attrs, handler ) {
                var arr = attrs.split( "|" ),
                    i = arr.length;

                while ( i-- ) {
                    Expr.attrHandle[ arr[ i ] ] = handler;
                }
            }

            /**
             * Checks document order of two siblings
             * @param {Element} a
             * @param {Element} b
             * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
             */
            function siblingCheck( a, b ) {
                var cur = b && a,
                    diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
                        a.sourceIndex - b.sourceIndex;

                // Use IE sourceIndex if available on both nodes
                if ( diff ) {
                    return diff;
                }

                // Check if b follows a
                if ( cur ) {
                    while ( ( cur = cur.nextSibling ) ) {
                        if ( cur === b ) {
                            return -1;
                        }
                    }
                }

                return a ? 1 : -1;
            }

            /**
             * Returns a function to use in pseudos for input types
             * @param {String} type
             */
            function createInputPseudo( type ) {
                return function( elem ) {
                    var name = elem.nodeName.toLowerCase();
                    return name === "input" && elem.type === type;
                };
            }

            /**
             * Returns a function to use in pseudos for buttons
             * @param {String} type
             */
            function createButtonPseudo( type ) {
                return function( elem ) {
                    var name = elem.nodeName.toLowerCase();
                    return ( name === "input" || name === "button" ) && elem.type === type;
                };
            }

            /**
             * Returns a function to use in pseudos for :enabled/:disabled
             * @param {Boolean} disabled true for :disabled; false for :enabled
             */
            function createDisabledPseudo( disabled ) {

                // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
                return function( elem ) {

                    // Only certain elements can match :enabled or :disabled
                    // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
                    // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
                    if ( "form" in elem ) {

                        // Check for inherited disabledness on relevant non-disabled elements:
                        // * listed form-associated elements in a disabled fieldset
                        //   https://html.spec.whatwg.org/multipage/forms.html#category-listed
                        //   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
                        // * option elements in a disabled optgroup
                        //   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
                        // All such elements have a "form" property.
                        if ( elem.parentNode && elem.disabled === false ) {

                            // Option elements defer to a parent optgroup if present
                            if ( "label" in elem ) {
                                if ( "label" in elem.parentNode ) {
                                    return elem.parentNode.disabled === disabled;
                                } else {
                                    return elem.disabled === disabled;
                                }
                            }

                            // Support: IE 6 - 11
                            // Use the isDisabled shortcut property to check for disabled fieldset ancestors
                            return elem.isDisabled === disabled ||

                                // Where there is no isDisabled, check manually
                                /* jshint -W018 */
                                elem.isDisabled !== !disabled &&
                                inDisabledFieldset( elem ) === disabled;
                        }

                        return elem.disabled === disabled;

                        // Try to winnow out elements that can't be disabled before trusting the disabled property.
                        // Some victims get caught in our net (label, legend, menu, track), but it shouldn't
                        // even exist on them, let alone have a boolean value.
                    } else if ( "label" in elem ) {
                        return elem.disabled === disabled;
                    }

                    // Remaining elements are neither :enabled nor :disabled
                    return false;
                };
            }

            /**
             * Returns a function to use in pseudos for positionals
             * @param {Function} fn
             */
            function createPositionalPseudo( fn ) {
                return markFunction( function( argument ) {
                    argument = +argument;
                    return markFunction( function( seed, matches ) {
                        var j,
                            matchIndexes = fn( [], seed.length, argument ),
                            i = matchIndexes.length;

                        // Match elements found at the specified indexes
                        while ( i-- ) {
                            if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
                                seed[ j ] = !( matches[ j ] = seed[ j ] );
                            }
                        }
                    } );
                } );
            }

            /**
             * Checks a node for validity as a Sizzle context
             * @param {Element|Object=} context
             * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
             */
            function testContext( context ) {
                return context && typeof context.getElementsByTagName !== "undefined" && context;
            }

// Expose support vars for convenience
            support = Sizzle.support = {};

            /**
             * Detects XML nodes
             * @param {Element|Object} elem An element or a document
             * @returns {Boolean} True iff elem is a non-HTML XML node
             */
            isXML = Sizzle.isXML = function( elem ) {
                var namespace = elem && elem.namespaceURI,
                    docElem = elem && ( elem.ownerDocument || elem ).documentElement;

                // Support: IE <=8
                // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
                // https://bugs.jquery.com/ticket/4833
                return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
            };

            /**
             * Sets document-related variables once based on the current document
             * @param {Element|Object} [doc] An element or document object to use to set the document
             * @returns {Object} Returns the current document
             */
            setDocument = Sizzle.setDocument = function( node ) {
                var hasCompare, subWindow,
                    doc = node ? node.ownerDocument || node : preferredDoc;

                // Return early if doc is invalid or already selected
                // Support: IE 11+, Edge 17 - 18+
                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                // two documents; shallow comparisons work.
                // eslint-disable-next-line eqeqeq
                if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
                    return document;
                }

                // Update global variables
                document = doc;
                docElem = document.documentElement;
                documentIsHTML = !isXML( document );

                // Support: IE 9 - 11+, Edge 12 - 18+
                // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
                // Support: IE 11+, Edge 17 - 18+
                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                // two documents; shallow comparisons work.
                // eslint-disable-next-line eqeqeq
                if ( preferredDoc != document &&
                    ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {

                    // Support: IE 11, Edge
                    if ( subWindow.addEventListener ) {
                        subWindow.addEventListener( "unload", unloadHandler, false );

                        // Support: IE 9 - 10 only
                    } else if ( subWindow.attachEvent ) {
                        subWindow.attachEvent( "onunload", unloadHandler );
                    }
                }

                // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
                // Safari 4 - 5 only, Opera <=11.6 - 12.x only
                // IE/Edge & older browsers don't support the :scope pseudo-class.
                // Support: Safari 6.0 only
                // Safari 6.0 supports :scope but it's an alias of :root there.
                support.scope = assert( function( el ) {
                    docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
                    return typeof el.querySelectorAll !== "undefined" &&
                        !el.querySelectorAll( ":scope fieldset div" ).length;
                } );

                /* Attributes
	---------------------------------------------------------------------- */

                // Support: IE<8
                // Verify that getAttribute really returns attributes and not properties
                // (excepting IE8 booleans)
                support.attributes = assert( function( el ) {
                    el.className = "i";
                    return !el.getAttribute( "className" );
                } );

                /* getElement(s)By*
	---------------------------------------------------------------------- */

                // Check if getElementsByTagName("*") returns only elements
                support.getElementsByTagName = assert( function( el ) {
                    el.appendChild( document.createComment( "" ) );
                    return !el.getElementsByTagName( "*" ).length;
                } );

                // Support: IE<9
                support.getElementsByClassName = rnative.test( document.getElementsByClassName );

                // Support: IE<10
                // Check if getElementById returns elements by name
                // The broken getElementById methods don't pick up programmatically-set names,
                // so use a roundabout getElementsByName test
                support.getById = assert( function( el ) {
                    docElem.appendChild( el ).id = expando;
                    return !document.getElementsByName || !document.getElementsByName( expando ).length;
                } );

                // ID filter and find
                if ( support.getById ) {
                    Expr.filter[ "ID" ] = function( id ) {
                        var attrId = id.replace( runescape, funescape );
                        return function( elem ) {
                            return elem.getAttribute( "id" ) === attrId;
                        };
                    };
                    Expr.find[ "ID" ] = function( id, context ) {
                        if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
                            var elem = context.getElementById( id );
                            return elem ? [ elem ] : [];
                        }
                    };
                } else {
                    Expr.filter[ "ID" ] =  function( id ) {
                        var attrId = id.replace( runescape, funescape );
                        return function( elem ) {
                            var node = typeof elem.getAttributeNode !== "undefined" &&
                                elem.getAttributeNode( "id" );
                            return node && node.value === attrId;
                        };
                    };

                    // Support: IE 6 - 7 only
                    // getElementById is not reliable as a find shortcut
                    Expr.find[ "ID" ] = function( id, context ) {
                        if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
                            var node, i, elems,
                                elem = context.getElementById( id );

                            if ( elem ) {

                                // Verify the id attribute
                                node = elem.getAttributeNode( "id" );
                                if ( node && node.value === id ) {
                                    return [ elem ];
                                }

                                // Fall back on getElementsByName
                                elems = context.getElementsByName( id );
                                i = 0;
                                while ( ( elem = elems[ i++ ] ) ) {
                                    node = elem.getAttributeNode( "id" );
                                    if ( node && node.value === id ) {
                                        return [ elem ];
                                    }
                                }
                            }

                            return [];
                        }
                    };
                }

                // Tag
                Expr.find[ "TAG" ] = support.getElementsByTagName ?
                    function( tag, context ) {
                        if ( typeof context.getElementsByTagName !== "undefined" ) {
                            return context.getElementsByTagName( tag );

                            // DocumentFragment nodes don't have gEBTN
                        } else if ( support.qsa ) {
                            return context.querySelectorAll( tag );
                        }
                    } :

                    function( tag, context ) {
                        var elem,
                            tmp = [],
                            i = 0,

                            // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
                            results = context.getElementsByTagName( tag );

                        // Filter out possible comments
                        if ( tag === "*" ) {
                            while ( ( elem = results[ i++ ] ) ) {
                                if ( elem.nodeType === 1 ) {
                                    tmp.push( elem );
                                }
                            }

                            return tmp;
                        }
                        return results;
                    };

                // Class
                Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) {
                    if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
                        return context.getElementsByClassName( className );
                    }
                };

                /* QSA/matchesSelector
	---------------------------------------------------------------------- */

                // QSA and matchesSelector support

                // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
                rbuggyMatches = [];

                // qSa(:focus) reports false when true (Chrome 21)
                // We allow this because of a bug in IE8/9 that throws an error
                // whenever `document.activeElement` is accessed on an iframe
                // So, we allow :focus to pass through QSA all the time to avoid the IE error
                // See https://bugs.jquery.com/ticket/13378
                rbuggyQSA = [];

                if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {

                    // Build QSA regex
                    // Regex strategy adopted from Diego Perini
                    assert( function( el ) {

                        var input;

                        // Select is set to empty string on purpose
                        // This is to test IE's treatment of not explicitly
                        // setting a boolean content attribute,
                        // since its presence should be enough
                        // https://bugs.jquery.com/ticket/12359
                        docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
                            "<select id='" + expando + "-\r\\' msallowcapture=''>" +
                            "<option selected=''></option></select>";

                        // Support: IE8, Opera 11-12.16
                        // Nothing should be selected when empty strings follow ^= or $= or *=
                        // The test attribute must be unknown in Opera but "safe" for WinRT
                        // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
                        if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
                            rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
                        }

                        // Support: IE8
                        // Boolean attributes and "value" are not treated correctly
                        if ( !el.querySelectorAll( "[selected]" ).length ) {
                            rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
                        }

                        // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
                        if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
                            rbuggyQSA.push( "~=" );
                        }

                        // Support: IE 11+, Edge 15 - 18+
                        // IE 11/Edge don't find elements on a `[name='']` query in some cases.
                        // Adding a temporary attribute to the document before the selection works
                        // around the issue.
                        // Interestingly, IE 10 & older don't seem to have the issue.
                        input = document.createElement( "input" );
                        input.setAttribute( "name", "" );
                        el.appendChild( input );
                        if ( !el.querySelectorAll( "[name='']" ).length ) {
                            rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
                                whitespace + "*(?:''|\"\")" );
                        }

                        // Webkit/Opera - :checked should return selected option elements
                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
                        // IE8 throws error here and will not see later tests
                        if ( !el.querySelectorAll( ":checked" ).length ) {
                            rbuggyQSA.push( ":checked" );
                        }

                        // Support: Safari 8+, iOS 8+
                        // https://bugs.webkit.org/show_bug.cgi?id=136851
                        // In-page `selector#id sibling-combinator selector` fails
                        if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
                            rbuggyQSA.push( ".#.+[+~]" );
                        }

                        // Support: Firefox <=3.6 - 5 only
                        // Old Firefox doesn't throw on a badly-escaped identifier.
                        el.querySelectorAll( "\\\f" );
                        rbuggyQSA.push( "[\\r\\n\\f]" );
                    } );

                    assert( function( el ) {
                        el.innerHTML = "<a href='' disabled='disabled'></a>" +
                            "<select disabled='disabled'><option/></select>";

                        // Support: Windows 8 Native Apps
                        // The type and name attributes are restricted during .innerHTML assignment
                        var input = document.createElement( "input" );
                        input.setAttribute( "type", "hidden" );
                        el.appendChild( input ).setAttribute( "name", "D" );

                        // Support: IE8
                        // Enforce case-sensitivity of name attribute
                        if ( el.querySelectorAll( "[name=d]" ).length ) {
                            rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
                        }

                        // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
                        // IE8 throws error here and will not see later tests
                        if ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
                            rbuggyQSA.push( ":enabled", ":disabled" );
                        }

                        // Support: IE9-11+
                        // IE's :disabled selector does not pick up the children of disabled fieldsets
                        docElem.appendChild( el ).disabled = true;
                        if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
                            rbuggyQSA.push( ":enabled", ":disabled" );
                        }

                        // Support: Opera 10 - 11 only
                        // Opera 10-11 does not throw on post-comma invalid pseudos
                        el.querySelectorAll( "*,:x" );
                        rbuggyQSA.push( ",.*:" );
                    } );
                }

                if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
                    docElem.webkitMatchesSelector ||
                    docElem.mozMatchesSelector ||
                    docElem.oMatchesSelector ||
                    docElem.msMatchesSelector ) ) ) ) {

                    assert( function( el ) {

                        // Check to see if it's possible to do matchesSelector
                        // on a disconnected node (IE 9)
                        support.disconnectedMatch = matches.call( el, "*" );

                        // This should fail with an exception
                        // Gecko does not error, returns false instead
                        matches.call( el, "[s!='']:x" );
                        rbuggyMatches.push( "!=", pseudos );
                    } );
                }

                rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
                rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );

                /* Contains
	---------------------------------------------------------------------- */
                hasCompare = rnative.test( docElem.compareDocumentPosition );

                // Element contains another
                // Purposefully self-exclusive
                // As in, an element does not contain itself
                contains = hasCompare || rnative.test( docElem.contains ) ?
                    function( a, b ) {
                        var adown = a.nodeType === 9 ? a.documentElement : a,
                            bup = b && b.parentNode;
                        return a === bup || !!( bup && bup.nodeType === 1 && (
                            adown.contains ?
                                adown.contains( bup ) :
                                a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
                        ) );
                    } :
                    function( a, b ) {
                        if ( b ) {
                            while ( ( b = b.parentNode ) ) {
                                if ( b === a ) {
                                    return true;
                                }
                            }
                        }
                        return false;
                    };

                /* Sorting
	---------------------------------------------------------------------- */

                // Document order sorting
                sortOrder = hasCompare ?
                    function( a, b ) {

                        // Flag for duplicate removal
                        if ( a === b ) {
                            hasDuplicate = true;
                            return 0;
                        }

                        // Sort on method existence if only one input has compareDocumentPosition
                        var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
                        if ( compare ) {
                            return compare;
                        }

                        // Calculate position if both inputs belong to the same document
                        // Support: IE 11+, Edge 17 - 18+
                        // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                        // two documents; shallow comparisons work.
                        // eslint-disable-next-line eqeqeq
                        compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
                            a.compareDocumentPosition( b ) :

                            // Otherwise we know they are disconnected
                            1;

                        // Disconnected nodes
                        if ( compare & 1 ||
                            ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {

                            // Choose the first element that is related to our preferred document
                            // Support: IE 11+, Edge 17 - 18+
                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                            // two documents; shallow comparisons work.
                            // eslint-disable-next-line eqeqeq
                            if ( a == document || a.ownerDocument == preferredDoc &&
                                contains( preferredDoc, a ) ) {
                                return -1;
                            }

                            // Support: IE 11+, Edge 17 - 18+
                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                            // two documents; shallow comparisons work.
                            // eslint-disable-next-line eqeqeq
                            if ( b == document || b.ownerDocument == preferredDoc &&
                                contains( preferredDoc, b ) ) {
                                return 1;
                            }

                            // Maintain original order
                            return sortInput ?
                                ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
                                0;
                        }

                        return compare & 4 ? -1 : 1;
                    } :
                    function( a, b ) {

                        // Exit early if the nodes are identical
                        if ( a === b ) {
                            hasDuplicate = true;
                            return 0;
                        }

                        var cur,
                            i = 0,
                            aup = a.parentNode,
                            bup = b.parentNode,
                            ap = [ a ],
                            bp = [ b ];

                        // Parentless nodes are either documents or disconnected
                        if ( !aup || !bup ) {

                            // Support: IE 11+, Edge 17 - 18+
                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                            // two documents; shallow comparisons work.
                            /* eslint-disable eqeqeq */
                            return a == document ? -1 :
                                b == document ? 1 :
                                    /* eslint-enable eqeqeq */
                                    aup ? -1 :
                                        bup ? 1 :
                                            sortInput ?
                                                ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
                                                0;

                            // If the nodes are siblings, we can do a quick check
                        } else if ( aup === bup ) {
                            return siblingCheck( a, b );
                        }

                        // Otherwise we need full lists of their ancestors for comparison
                        cur = a;
                        while ( ( cur = cur.parentNode ) ) {
                            ap.unshift( cur );
                        }
                        cur = b;
                        while ( ( cur = cur.parentNode ) ) {
                            bp.unshift( cur );
                        }

                        // Walk down the tree looking for a discrepancy
                        while ( ap[ i ] === bp[ i ] ) {
                            i++;
                        }

                        return i ?

                            // Do a sibling check if the nodes have a common ancestor
                            siblingCheck( ap[ i ], bp[ i ] ) :

                            // Otherwise nodes in our document sort first
                            // Support: IE 11+, Edge 17 - 18+
                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                            // two documents; shallow comparisons work.
                            /* eslint-disable eqeqeq */
                            ap[ i ] == preferredDoc ? -1 :
                                bp[ i ] == preferredDoc ? 1 :
                                    /* eslint-enable eqeqeq */
                                    0;
                    };

                return document;
            };

            Sizzle.matches = function( expr, elements ) {
                return Sizzle( expr, null, null, elements );
            };

            Sizzle.matchesSelector = function( elem, expr ) {
                setDocument( elem );

                if ( support.matchesSelector && documentIsHTML &&
                    !nonnativeSelectorCache[ expr + " " ] &&
                    ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
                    ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {

                    try {
                        var ret = matches.call( elem, expr );

                        // IE 9's matchesSelector returns false on disconnected nodes
                        if ( ret || support.disconnectedMatch ||

                            // As well, disconnected nodes are said to be in a document
                            // fragment in IE 9
                            elem.document && elem.document.nodeType !== 11 ) {
                            return ret;
                        }
                    } catch ( e ) {
                        nonnativeSelectorCache( expr, true );
                    }
                }

                return Sizzle( expr, document, null, [ elem ] ).length > 0;
            };

            Sizzle.contains = function( context, elem ) {

                // Set document vars if needed
                // Support: IE 11+, Edge 17 - 18+
                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                // two documents; shallow comparisons work.
                // eslint-disable-next-line eqeqeq
                if ( ( context.ownerDocument || context ) != document ) {
                    setDocument( context );
                }
                return contains( context, elem );
            };

            Sizzle.attr = function( elem, name ) {

                // Set document vars if needed
                // Support: IE 11+, Edge 17 - 18+
                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                // two documents; shallow comparisons work.
                // eslint-disable-next-line eqeqeq
                if ( ( elem.ownerDocument || elem ) != document ) {
                    setDocument( elem );
                }

                var fn = Expr.attrHandle[ name.toLowerCase() ],

                    // Don't get fooled by Object.prototype properties (jQuery #13807)
                    val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
                        fn( elem, name, !documentIsHTML ) :
                        undefined;

                return val !== undefined ?
                    val :
                    support.attributes || !documentIsHTML ?
                        elem.getAttribute( name ) :
                        ( val = elem.getAttributeNode( name ) ) && val.specified ?
                            val.value :
                            null;
            };

            Sizzle.escape = function( sel ) {
                return ( sel + "" ).replace( rcssescape, fcssescape );
            };

            Sizzle.error = function( msg ) {
                throw new Error( "Syntax error, unrecognized expression: " + msg );
            };

            /**
             * Document sorting and removing duplicates
             * @param {ArrayLike} results
             */
            Sizzle.uniqueSort = function( results ) {
                var elem,
                    duplicates = [],
                    j = 0,
                    i = 0;

                // Unless we *know* we can detect duplicates, assume their presence
                hasDuplicate = !support.detectDuplicates;
                sortInput = !support.sortStable && results.slice( 0 );
                results.sort( sortOrder );

                if ( hasDuplicate ) {
                    while ( ( elem = results[ i++ ] ) ) {
                        if ( elem === results[ i ] ) {
                            j = duplicates.push( i );
                        }
                    }
                    while ( j-- ) {
                        results.splice( duplicates[ j ], 1 );
                    }
                }

                // Clear input after sorting to release objects
                // See https://github.com/jquery/sizzle/pull/225
                sortInput = null;

                return results;
            };

            /**
             * Utility function for retrieving the text value of an array of DOM nodes
             * @param {Array|Element} elem
             */
            getText = Sizzle.getText = function( elem ) {
                var node,
                    ret = "",
                    i = 0,
                    nodeType = elem.nodeType;

                if ( !nodeType ) {

                    // If no nodeType, this is expected to be an array
                    while ( ( node = elem[ i++ ] ) ) {

                        // Do not traverse comment nodes
                        ret += getText( node );
                    }
                } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {

                    // Use textContent for elements
                    // innerText usage removed for consistency of new lines (jQuery #11153)
                    if ( typeof elem.textContent === "string" ) {
                        return elem.textContent;
                    } else {

                        // Traverse its children
                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
                            ret += getText( elem );
                        }
                    }
                } else if ( nodeType === 3 || nodeType === 4 ) {
                    return elem.nodeValue;
                }

                // Do not include comment or processing instruction nodes

                return ret;
            };

            Expr = Sizzle.selectors = {

                // Can be adjusted by the user
                cacheLength: 50,

                createPseudo: markFunction,

                match: matchExpr,

                attrHandle: {},

                find: {},

                relative: {
                    ">": { dir: "parentNode", first: true },
                    " ": { dir: "parentNode" },
                    "+": { dir: "previousSibling", first: true },
                    "~": { dir: "previousSibling" }
                },

                preFilter: {
                    "ATTR": function( match ) {
                        match[ 1 ] = match[ 1 ].replace( runescape, funescape );

                        // Move the given value to match[3] whether quoted or unquoted
                        match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
                            match[ 5 ] || "" ).replace( runescape, funescape );

                        if ( match[ 2 ] === "~=" ) {
                            match[ 3 ] = " " + match[ 3 ] + " ";
                        }

                        return match.slice( 0, 4 );
                    },

                    "CHILD": function( match ) {

                        /* matches from matchExpr["CHILD"]
				1 type (only|nth|...)
				2 what (child|of-type)
				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
				4 xn-component of xn+y argument ([+-]?\d*n|)
				5 sign of xn-component
				6 x of xn-component
				7 sign of y-component
				8 y of y-component
			*/
                        match[ 1 ] = match[ 1 ].toLowerCase();

                        if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {

                            // nth-* requires argument
                            if ( !match[ 3 ] ) {
                                Sizzle.error( match[ 0 ] );
                            }

                            // numeric x and y parameters for Expr.filter.CHILD
                            // remember that false/true cast respectively to 0/1
                            match[ 4 ] = +( match[ 4 ] ?
                                match[ 5 ] + ( match[ 6 ] || 1 ) :
                                2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
                            match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );

                            // other types prohibit arguments
                        } else if ( match[ 3 ] ) {
                            Sizzle.error( match[ 0 ] );
                        }

                        return match;
                    },

                    "PSEUDO": function( match ) {
                        var excess,
                            unquoted = !match[ 6 ] && match[ 2 ];

                        if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
                            return null;
                        }

                        // Accept quoted arguments as-is
                        if ( match[ 3 ] ) {
                            match[ 2 ] = match[ 4 ] || match[ 5 ] || "";

                            // Strip excess characters from unquoted arguments
                        } else if ( unquoted && rpseudo.test( unquoted ) &&

                            // Get excess from tokenize (recursively)
                            ( excess = tokenize( unquoted, true ) ) &&

                            // advance to the next closing parenthesis
                            ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {

                            // excess is a negative index
                            match[ 0 ] = match[ 0 ].slice( 0, excess );
                            match[ 2 ] = unquoted.slice( 0, excess );
                        }

                        // Return only captures needed by the pseudo filter method (type and argument)
                        return match.slice( 0, 3 );
                    }
                },

                filter: {

                    "TAG": function( nodeNameSelector ) {
                        var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
                        return nodeNameSelector === "*" ?
                            function() {
                                return true;
                            } :
                            function( elem ) {
                                return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
                            };
                    },

                    "CLASS": function( className ) {
                        var pattern = classCache[ className + " " ];

                        return pattern ||
                            ( pattern = new RegExp( "(^|" + whitespace +
                                ")" + className + "(" + whitespace + "|$)" ) ) && classCache(
                                className, function( elem ) {
                                    return pattern.test(
                                        typeof elem.className === "string" && elem.className ||
                                        typeof elem.getAttribute !== "undefined" &&
                                        elem.getAttribute( "class" ) ||
                                        ""
                                    );
                                } );
                    },

                    "ATTR": function( name, operator, check ) {
                        return function( elem ) {
                            var result = Sizzle.attr( elem, name );

                            if ( result == null ) {
                                return operator === "!=";
                            }
                            if ( !operator ) {
                                return true;
                            }

                            result += "";

                            /* eslint-disable max-len */

                            return operator === "=" ? result === check :
                                operator === "!=" ? result !== check :
                                    operator === "^=" ? check && result.indexOf( check ) === 0 :
                                        operator === "*=" ? check && result.indexOf( check ) > -1 :
                                            operator === "$=" ? check && result.slice( -check.length ) === check :
                                                operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
                                                    operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
                                                        false;
                            /* eslint-enable max-len */

                        };
                    },

                    "CHILD": function( type, what, _argument, first, last ) {
                        var simple = type.slice( 0, 3 ) !== "nth",
                            forward = type.slice( -4 ) !== "last",
                            ofType = what === "of-type";

                        return first === 1 && last === 0 ?

                            // Shortcut for :nth-*(n)
                            function( elem ) {
                                return !!elem.parentNode;
                            } :

                            function( elem, _context, xml ) {
                                var cache, uniqueCache, outerCache, node, nodeIndex, start,
                                    dir = simple !== forward ? "nextSibling" : "previousSibling",
                                    parent = elem.parentNode,
                                    name = ofType && elem.nodeName.toLowerCase(),
                                    useCache = !xml && !ofType,
                                    diff = false;

                                if ( parent ) {

                                    // :(first|last|only)-(child|of-type)
                                    if ( simple ) {
                                        while ( dir ) {
                                            node = elem;
                                            while ( ( node = node[ dir ] ) ) {
                                                if ( ofType ?
                                                    node.nodeName.toLowerCase() === name :
                                                    node.nodeType === 1 ) {

                                                    return false;
                                                }
                                            }

                                            // Reverse direction for :only-* (if we haven't yet done so)
                                            start = dir = type === "only" && !start && "nextSibling";
                                        }
                                        return true;
                                    }

                                    start = [ forward ? parent.firstChild : parent.lastChild ];

                                    // non-xml :nth-child(...) stores cache data on `parent`
                                    if ( forward && useCache ) {

                                        // Seek `elem` from a previously-cached index

                                        // ...in a gzip-friendly way
                                        node = parent;
                                        outerCache = node[ expando ] || ( node[ expando ] = {} );

                                        // Support: IE <9 only
                                        // Defend against cloned attroperties (jQuery gh-1709)
                                        uniqueCache = outerCache[ node.uniqueID ] ||
                                            ( outerCache[ node.uniqueID ] = {} );

                                        cache = uniqueCache[ type ] || [];
                                        nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
                                        diff = nodeIndex && cache[ 2 ];
                                        node = nodeIndex && parent.childNodes[ nodeIndex ];

                                        while ( ( node = ++nodeIndex && node && node[ dir ] ||

                                            // Fallback to seeking `elem` from the start
                                            ( diff = nodeIndex = 0 ) || start.pop() ) ) {

                                            // When found, cache indexes on `parent` and break
                                            if ( node.nodeType === 1 && ++diff && node === elem ) {
                                                uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
                                                break;
                                            }
                                        }

                                    } else {

                                        // Use previously-cached element index if available
                                        if ( useCache ) {

                                            // ...in a gzip-friendly way
                                            node = elem;
                                            outerCache = node[ expando ] || ( node[ expando ] = {} );

                                            // Support: IE <9 only
                                            // Defend against cloned attroperties (jQuery gh-1709)
                                            uniqueCache = outerCache[ node.uniqueID ] ||
                                                ( outerCache[ node.uniqueID ] = {} );

                                            cache = uniqueCache[ type ] || [];
                                            nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
                                            diff = nodeIndex;
                                        }

                                        // xml :nth-child(...)
                                        // or :nth-last-child(...) or :nth(-last)?-of-type(...)
                                        if ( diff === false ) {

                                            // Use the same loop as above to seek `elem` from the start
                                            while ( ( node = ++nodeIndex && node && node[ dir ] ||
                                                ( diff = nodeIndex = 0 ) || start.pop() ) ) {

                                                if ( ( ofType ?
                                                        node.nodeName.toLowerCase() === name :
                                                        node.nodeType === 1 ) &&
                                                    ++diff ) {

                                                    // Cache the index of each encountered element
                                                    if ( useCache ) {
                                                        outerCache = node[ expando ] ||
                                                            ( node[ expando ] = {} );

                                                        // Support: IE <9 only
                                                        // Defend against cloned attroperties (jQuery gh-1709)
                                                        uniqueCache = outerCache[ node.uniqueID ] ||
                                                            ( outerCache[ node.uniqueID ] = {} );

                                                        uniqueCache[ type ] = [ dirruns, diff ];
                                                    }

                                                    if ( node === elem ) {
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    // Incorporate the offset, then check against cycle size
                                    diff -= last;
                                    return diff === first || ( diff % first === 0 && diff / first >= 0 );
                                }
                            };
                    },

                    "PSEUDO": function( pseudo, argument ) {

                        // pseudo-class names are case-insensitive
                        // http://www.w3.org/TR/selectors/#pseudo-classes
                        // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
                        // Remember that setFilters inherits from pseudos
                        var args,
                            fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
                                Sizzle.error( "unsupported pseudo: " + pseudo );

                        // The user may use createPseudo to indicate that
                        // arguments are needed to create the filter function
                        // just as Sizzle does
                        if ( fn[ expando ] ) {
                            return fn( argument );
                        }

                        // But maintain support for old signatures
                        if ( fn.length > 1 ) {
                            args = [ pseudo, pseudo, "", argument ];
                            return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
                                markFunction( function( seed, matches ) {
                                    var idx,
                                        matched = fn( seed, argument ),
                                        i = matched.length;
                                    while ( i-- ) {
                                        idx = indexOf( seed, matched[ i ] );
                                        seed[ idx ] = !( matches[ idx ] = matched[ i ] );
                                    }
                                } ) :
                                function( elem ) {
                                    return fn( elem, 0, args );
                                };
                        }

                        return fn;
                    }
                },

                pseudos: {

                    // Potentially complex pseudos
                    "not": markFunction( function( selector ) {

                        // Trim the selector passed to compile
                        // to avoid treating leading and trailing
                        // spaces as combinators
                        var input = [],
                            results = [],
                            matcher = compile( selector.replace( rtrim, "$1" ) );

                        return matcher[ expando ] ?
                            markFunction( function( seed, matches, _context, xml ) {
                                var elem,
                                    unmatched = matcher( seed, null, xml, [] ),
                                    i = seed.length;

                                // Match elements unmatched by `matcher`
                                while ( i-- ) {
                                    if ( ( elem = unmatched[ i ] ) ) {
                                        seed[ i ] = !( matches[ i ] = elem );
                                    }
                                }
                            } ) :
                            function( elem, _context, xml ) {
                                input[ 0 ] = elem;
                                matcher( input, null, xml, results );

                                // Don't keep the element (issue #299)
                                input[ 0 ] = null;
                                return !results.pop();
                            };
                    } ),

                    "has": markFunction( function( selector ) {
                        return function( elem ) {
                            return Sizzle( selector, elem ).length > 0;
                        };
                    } ),

                    "contains": markFunction( function( text ) {
                        text = text.replace( runescape, funescape );
                        return function( elem ) {
                            return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
                        };
                    } ),

                    // "Whether an element is represented by a :lang() selector
                    // is based solely on the element's language value
                    // being equal to the identifier C,
                    // or beginning with the identifier C immediately followed by "-".
                    // The matching of C against the element's language value is performed case-insensitively.
                    // The identifier C does not have to be a valid language name."
                    // http://www.w3.org/TR/selectors/#lang-pseudo
                    "lang": markFunction( function( lang ) {

                        // lang value must be a valid identifier
                        if ( !ridentifier.test( lang || "" ) ) {
                            Sizzle.error( "unsupported lang: " + lang );
                        }
                        lang = lang.replace( runescape, funescape ).toLowerCase();
                        return function( elem ) {
                            var elemLang;
                            do {
                                if ( ( elemLang = documentIsHTML ?
                                    elem.lang :
                                    elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {

                                    elemLang = elemLang.toLowerCase();
                                    return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
                                }
                            } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
                            return false;
                        };
                    } ),

                    // Miscellaneous
                    "target": function( elem ) {
                        var hash = window.location && window.location.hash;
                        return hash && hash.slice( 1 ) === elem.id;
                    },

                    "root": function( elem ) {
                        return elem === docElem;
                    },

                    "focus": function( elem ) {
                        return elem === document.activeElement &&
                            ( !document.hasFocus || document.hasFocus() ) &&
                            !!( elem.type || elem.href || ~elem.tabIndex );
                    },

                    // Boolean properties
                    "enabled": createDisabledPseudo( false ),
                    "disabled": createDisabledPseudo( true ),

                    "checked": function( elem ) {

                        // In CSS3, :checked should return both checked and selected elements
                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
                        var nodeName = elem.nodeName.toLowerCase();
                        return ( nodeName === "input" && !!elem.checked ) ||
                            ( nodeName === "option" && !!elem.selected );
                    },

                    "selected": function( elem ) {

                        // Accessing this property makes selected-by-default
                        // options in Safari work properly
                        if ( elem.parentNode ) {
                            // eslint-disable-next-line no-unused-expressions
                            elem.parentNode.selectedIndex;
                        }

                        return elem.selected === true;
                    },

                    // Contents
                    "empty": function( elem ) {

                        // http://www.w3.org/TR/selectors/#empty-pseudo
                        // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
                        //   but not by others (comment: 8; processing instruction: 7; etc.)
                        // nodeType < 6 works because attributes (2) do not appear as children
                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
                            if ( elem.nodeType < 6 ) {
                                return false;
                            }
                        }
                        return true;
                    },

                    "parent": function( elem ) {
                        return !Expr.pseudos[ "empty" ]( elem );
                    },

                    // Element/input types
                    "header": function( elem ) {
                        return rheader.test( elem.nodeName );
                    },

                    "input": function( elem ) {
                        return rinputs.test( elem.nodeName );
                    },

                    "button": function( elem ) {
                        var name = elem.nodeName.toLowerCase();
                        return name === "input" && elem.type === "button" || name === "button";
                    },

                    "text": function( elem ) {
                        var attr;
                        return elem.nodeName.toLowerCase() === "input" &&
                            elem.type === "text" &&

                            // Support: IE<8
                            // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
                            ( ( attr = elem.getAttribute( "type" ) ) == null ||
                                attr.toLowerCase() === "text" );
                    },

                    // Position-in-collection
                    "first": createPositionalPseudo( function() {
                        return [ 0 ];
                    } ),

                    "last": createPositionalPseudo( function( _matchIndexes, length ) {
                        return [ length - 1 ];
                    } ),

                    "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
                        return [ argument < 0 ? argument + length : argument ];
                    } ),

                    "even": createPositionalPseudo( function( matchIndexes, length ) {
                        var i = 0;
                        for ( ; i < length; i += 2 ) {
                            matchIndexes.push( i );
                        }
                        return matchIndexes;
                    } ),

                    "odd": createPositionalPseudo( function( matchIndexes, length ) {
                        var i = 1;
                        for ( ; i < length; i += 2 ) {
                            matchIndexes.push( i );
                        }
                        return matchIndexes;
                    } ),

                    "lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
                        var i = argument < 0 ?
                            argument + length :
                            argument > length ?
                                length :
                                argument;
                        for ( ; --i >= 0; ) {
                            matchIndexes.push( i );
                        }
                        return matchIndexes;
                    } ),

                    "gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
                        var i = argument < 0 ? argument + length : argument;
                        for ( ; ++i < length; ) {
                            matchIndexes.push( i );
                        }
                        return matchIndexes;
                    } )
                }
            };

            Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ];

// Add button/input type pseudos
            for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
                Expr.pseudos[ i ] = createInputPseudo( i );
            }
            for ( i in { submit: true, reset: true } ) {
                Expr.pseudos[ i ] = createButtonPseudo( i );
            }

// Easy API for creating new setFilters
            function setFilters() {}
            setFilters.prototype = Expr.filters = Expr.pseudos;
            Expr.setFilters = new setFilters();

            tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
                var matched, match, tokens, type,
                    soFar, groups, preFilters,
                    cached = tokenCache[ selector + " " ];

                if ( cached ) {
                    return parseOnly ? 0 : cached.slice( 0 );
                }

                soFar = selector;
                groups = [];
                preFilters = Expr.preFilter;

                while ( soFar ) {

                    // Comma and first run
                    if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
                        if ( match ) {

                            // Don't consume trailing commas as valid
                            soFar = soFar.slice( match[ 0 ].length ) || soFar;
                        }
                        groups.push( ( tokens = [] ) );
                    }

                    matched = false;

                    // Combinators
                    if ( ( match = rcombinators.exec( soFar ) ) ) {
                        matched = match.shift();
                        tokens.push( {
                            value: matched,

                            // Cast descendant combinators to space
                            type: match[ 0 ].replace( rtrim, " " )
                        } );
                        soFar = soFar.slice( matched.length );
                    }

                    // Filters
                    for ( type in Expr.filter ) {
                        if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
                            ( match = preFilters[ type ]( match ) ) ) ) {
                            matched = match.shift();
                            tokens.push( {
                                value: matched,
                                type: type,
                                matches: match
                            } );
                            soFar = soFar.slice( matched.length );
                        }
                    }

                    if ( !matched ) {
                        break;
                    }
                }

                // Return the length of the invalid excess
                // if we're just parsing
                // Otherwise, throw an error or return tokens
                return parseOnly ?
                    soFar.length :
                    soFar ?
                        Sizzle.error( selector ) :

                        // Cache the tokens
                        tokenCache( selector, groups ).slice( 0 );
            };

            function toSelector( tokens ) {
                var i = 0,
                    len = tokens.length,
                    selector = "";
                for ( ; i < len; i++ ) {
                    selector += tokens[ i ].value;
                }
                return selector;
            }

            function addCombinator( matcher, combinator, base ) {
                var dir = combinator.dir,
                    skip = combinator.next,
                    key = skip || dir,
                    checkNonElements = base && key === "parentNode",
                    doneName = done++;

                return combinator.first ?

                    // Check against closest ancestor/preceding element
                    function( elem, context, xml ) {
                        while ( ( elem = elem[ dir ] ) ) {
                            if ( elem.nodeType === 1 || checkNonElements ) {
                                return matcher( elem, context, xml );
                            }
                        }
                        return false;
                    } :

                    // Check against all ancestor/preceding elements
                    function( elem, context, xml ) {
                        var oldCache, uniqueCache, outerCache,
                            newCache = [ dirruns, doneName ];

                        // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
                        if ( xml ) {
                            while ( ( elem = elem[ dir ] ) ) {
                                if ( elem.nodeType === 1 || checkNonElements ) {
                                    if ( matcher( elem, context, xml ) ) {
                                        return true;
                                    }
                                }
                            }
                        } else {
                            while ( ( elem = elem[ dir ] ) ) {
                                if ( elem.nodeType === 1 || checkNonElements ) {
                                    outerCache = elem[ expando ] || ( elem[ expando ] = {} );

                                    // Support: IE <9 only
                                    // Defend against cloned attroperties (jQuery gh-1709)
                                    uniqueCache = outerCache[ elem.uniqueID ] ||
                                        ( outerCache[ elem.uniqueID ] = {} );

                                    if ( skip && skip === elem.nodeName.toLowerCase() ) {
                                        elem = elem[ dir ] || elem;
                                    } else if ( ( oldCache = uniqueCache[ key ] ) &&
                                        oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {

                                        // Assign to newCache so results back-propagate to previous elements
                                        return ( newCache[ 2 ] = oldCache[ 2 ] );
                                    } else {

                                        // Reuse newcache so results back-propagate to previous elements
                                        uniqueCache[ key ] = newCache;

                                        // A match means we're done; a fail means we have to keep checking
                                        if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
                                            return true;
                                        }
                                    }
                                }
                            }
                        }
                        return false;
                    };
            }

            function elementMatcher( matchers ) {
                return matchers.length > 1 ?
                    function( elem, context, xml ) {
                        var i = matchers.length;
                        while ( i-- ) {
                            if ( !matchers[ i ]( elem, context, xml ) ) {
                                return false;
                            }
                        }
                        return true;
                    } :
                    matchers[ 0 ];
            }

            function multipleContexts( selector, contexts, results ) {
                var i = 0,
                    len = contexts.length;
                for ( ; i < len; i++ ) {
                    Sizzle( selector, contexts[ i ], results );
                }
                return results;
            }

            function condense( unmatched, map, filter, context, xml ) {
                var elem,
                    newUnmatched = [],
                    i = 0,
                    len = unmatched.length,
                    mapped = map != null;

                for ( ; i < len; i++ ) {
                    if ( ( elem = unmatched[ i ] ) ) {
                        if ( !filter || filter( elem, context, xml ) ) {
                            newUnmatched.push( elem );
                            if ( mapped ) {
                                map.push( i );
                            }
                        }
                    }
                }

                return newUnmatched;
            }

            function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
                if ( postFilter && !postFilter[ expando ] ) {
                    postFilter = setMatcher( postFilter );
                }
                if ( postFinder && !postFinder[ expando ] ) {
                    postFinder = setMatcher( postFinder, postSelector );
                }
                return markFunction( function( seed, results, context, xml ) {
                    var temp, i, elem,
                        preMap = [],
                        postMap = [],
                        preexisting = results.length,

                        // Get initial elements from seed or context
                        elems = seed || multipleContexts(
                            selector || "*",
                            context.nodeType ? [ context ] : context,
                            []
                        ),

                        // Prefilter to get matcher input, preserving a map for seed-results synchronization
                        matcherIn = preFilter && ( seed || !selector ) ?
                            condense( elems, preMap, preFilter, context, xml ) :
                            elems,

                        matcherOut = matcher ?

                            // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
                            postFinder || ( seed ? preFilter : preexisting || postFilter ) ?

                                // ...intermediate processing is necessary
                                [] :

                                // ...otherwise use results directly
                                results :
                            matcherIn;

                    // Find primary matches
                    if ( matcher ) {
                        matcher( matcherIn, matcherOut, context, xml );
                    }

                    // Apply postFilter
                    if ( postFilter ) {
                        temp = condense( matcherOut, postMap );
                        postFilter( temp, [], context, xml );

                        // Un-match failing elements by moving them back to matcherIn
                        i = temp.length;
                        while ( i-- ) {
                            if ( ( elem = temp[ i ] ) ) {
                                matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
                            }
                        }
                    }

                    if ( seed ) {
                        if ( postFinder || preFilter ) {
                            if ( postFinder ) {

                                // Get the final matcherOut by condensing this intermediate into postFinder contexts
                                temp = [];
                                i = matcherOut.length;
                                while ( i-- ) {
                                    if ( ( elem = matcherOut[ i ] ) ) {

                                        // Restore matcherIn since elem is not yet a final match
                                        temp.push( ( matcherIn[ i ] = elem ) );
                                    }
                                }
                                postFinder( null, ( matcherOut = [] ), temp, xml );
                            }

                            // Move matched elements from seed to results to keep them synchronized
                            i = matcherOut.length;
                            while ( i-- ) {
                                if ( ( elem = matcherOut[ i ] ) &&
                                    ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {

                                    seed[ temp ] = !( results[ temp ] = elem );
                                }
                            }
                        }

                        // Add elements to results, through postFinder if defined
                    } else {
                        matcherOut = condense(
                            matcherOut === results ?
                                matcherOut.splice( preexisting, matcherOut.length ) :
                                matcherOut
                        );
                        if ( postFinder ) {
                            postFinder( null, results, matcherOut, xml );
                        } else {
                            push.apply( results, matcherOut );
                        }
                    }
                } );
            }

            function matcherFromTokens( tokens ) {
                var checkContext, matcher, j,
                    len = tokens.length,
                    leadingRelative = Expr.relative[ tokens[ 0 ].type ],
                    implicitRelative = leadingRelative || Expr.relative[ " " ],
                    i = leadingRelative ? 1 : 0,

                    // The foundational matcher ensures that elements are reachable from top-level context(s)
                    matchContext = addCombinator( function( elem ) {
                        return elem === checkContext;
                    }, implicitRelative, true ),
                    matchAnyContext = addCombinator( function( elem ) {
                        return indexOf( checkContext, elem ) > -1;
                    }, implicitRelative, true ),
                    matchers = [ function( elem, context, xml ) {
                        var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
                            ( checkContext = context ).nodeType ?
                                matchContext( elem, context, xml ) :
                                matchAnyContext( elem, context, xml ) );

                        // Avoid hanging onto element (issue #299)
                        checkContext = null;
                        return ret;
                    } ];

                for ( ; i < len; i++ ) {
                    if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
                        matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
                    } else {
                        matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );

                        // Return special upon seeing a positional matcher
                        if ( matcher[ expando ] ) {

                            // Find the next relative operator (if any) for proper handling
                            j = ++i;
                            for ( ; j < len; j++ ) {
                                if ( Expr.relative[ tokens[ j ].type ] ) {
                                    break;
                                }
                            }
                            return setMatcher(
                                i > 1 && elementMatcher( matchers ),
                                i > 1 && toSelector(

                                    // If the preceding token was a descendant combinator, insert an implicit any-element `*`
                                    tokens
                                        .slice( 0, i - 1 )
                                        .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
                                ).replace( rtrim, "$1" ),
                                matcher,
                                i < j && matcherFromTokens( tokens.slice( i, j ) ),
                                j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
                                j < len && toSelector( tokens )
                            );
                        }
                        matchers.push( matcher );
                    }
                }

                return elementMatcher( matchers );
            }

            function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
                var bySet = setMatchers.length > 0,
                    byElement = elementMatchers.length > 0,
                    superMatcher = function( seed, context, xml, results, outermost ) {
                        var elem, j, matcher,
                            matchedCount = 0,
                            i = "0",
                            unmatched = seed && [],
                            setMatched = [],
                            contextBackup = outermostContext,

                            // We must always have either seed elements or outermost context
                            elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ),

                            // Use integer dirruns iff this is the outermost matcher
                            dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
                            len = elems.length;

                        if ( outermost ) {

                            // Support: IE 11+, Edge 17 - 18+
                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                            // two documents; shallow comparisons work.
                            // eslint-disable-next-line eqeqeq
                            outermostContext = context == document || context || outermost;
                        }

                        // Add elements passing elementMatchers directly to results
                        // Support: IE<9, Safari
                        // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
                        for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
                            if ( byElement && elem ) {
                                j = 0;

                                // Support: IE 11+, Edge 17 - 18+
                                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
                                // two documents; shallow comparisons work.
                                // eslint-disable-next-line eqeqeq
                                if ( !context && elem.ownerDocument != document ) {
                                    setDocument( elem );
                                    xml = !documentIsHTML;
                                }
                                while ( ( matcher = elementMatchers[ j++ ] ) ) {
                                    if ( matcher( elem, context || document, xml ) ) {
                                        results.push( elem );
                                        break;
                                    }
                                }
                                if ( outermost ) {
                                    dirruns = dirrunsUnique;
                                }
                            }

                            // Track unmatched elements for set filters
                            if ( bySet ) {

                                // They will have gone through all possible matchers
                                if ( ( elem = !matcher && elem ) ) {
                                    matchedCount--;
                                }

                                // Lengthen the array for every element, matched or not
                                if ( seed ) {
                                    unmatched.push( elem );
                                }
                            }
                        }

                        // `i` is now the count of elements visited above, and adding it to `matchedCount`
                        // makes the latter nonnegative.
                        matchedCount += i;

                        // Apply set filters to unmatched elements
                        // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
                        // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
                        // no element matchers and no seed.
                        // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
                        // case, which will result in a "00" `matchedCount` that differs from `i` but is also
                        // numerically zero.
                        if ( bySet && i !== matchedCount ) {
                            j = 0;
                            while ( ( matcher = setMatchers[ j++ ] ) ) {
                                matcher( unmatched, setMatched, context, xml );
                            }

                            if ( seed ) {

                                // Reintegrate element matches to eliminate the need for sorting
                                if ( matchedCount > 0 ) {
                                    while ( i-- ) {
                                        if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
                                            setMatched[ i ] = pop.call( results );
                                        }
                                    }
                                }

                                // Discard index placeholder values to get only actual matches
                                setMatched = condense( setMatched );
                            }

                            // Add matches to results
                            push.apply( results, setMatched );

                            // Seedless set matches succeeding multiple successful matchers stipulate sorting
                            if ( outermost && !seed && setMatched.length > 0 &&
                                ( matchedCount + setMatchers.length ) > 1 ) {

                                Sizzle.uniqueSort( results );
                            }
                        }

                        // Override manipulation of globals by nested matchers
                        if ( outermost ) {
                            dirruns = dirrunsUnique;
                            outermostContext = contextBackup;
                        }

                        return unmatched;
                    };

                return bySet ?
                    markFunction( superMatcher ) :
                    superMatcher;
            }

            compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
                var i,
                    setMatchers = [],
                    elementMatchers = [],
                    cached = compilerCache[ selector + " " ];

                if ( !cached ) {

                    // Generate a function of recursive functions that can be used to check each element
                    if ( !match ) {
                        match = tokenize( selector );
                    }
                    i = match.length;
                    while ( i-- ) {
                        cached = matcherFromTokens( match[ i ] );
                        if ( cached[ expando ] ) {
                            setMatchers.push( cached );
                        } else {
                            elementMatchers.push( cached );
                        }
                    }

                    // Cache the compiled function
                    cached = compilerCache(
                        selector,
                        matcherFromGroupMatchers( elementMatchers, setMatchers )
                    );

                    // Save selector and tokenization
                    cached.selector = selector;
                }
                return cached;
            };

            /**
             * A low-level selection function that works with Sizzle's compiled
             *  selector functions
             * @param {String|Function} selector A selector or a pre-compiled
             *  selector function built with Sizzle.compile
             * @param {Element} context
             * @param {Array} [results]
             * @param {Array} [seed] A set of elements to match against
             */
            select = Sizzle.select = function( selector, context, results, seed ) {
                var i, tokens, token, type, find,
                    compiled = typeof selector === "function" && selector,
                    match = !seed && tokenize( ( selector = compiled.selector || selector ) );

                results = results || [];

                // Try to minimize operations if there is only one selector in the list and no seed
                // (the latter of which guarantees us context)
                if ( match.length === 1 ) {

                    // Reduce context if the leading compound selector is an ID
                    tokens = match[ 0 ] = match[ 0 ].slice( 0 );
                    if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
                        context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {

                        context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
                            .replace( runescape, funescape ), context ) || [] )[ 0 ];
                        if ( !context ) {
                            return results;

                            // Precompiled matchers will still verify ancestry, so step up a level
                        } else if ( compiled ) {
                            context = context.parentNode;
                        }

                        selector = selector.slice( tokens.shift().value.length );
                    }

                    // Fetch a seed set for right-to-left matching
                    i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;
                    while ( i-- ) {
                        token = tokens[ i ];

                        // Abort if we hit a combinator
                        if ( Expr.relative[ ( type = token.type ) ] ) {
                            break;
                        }
                        if ( ( find = Expr.find[ type ] ) ) {

                            // Search, expanding context for leading sibling combinators
                            if ( ( seed = find(
                                token.matches[ 0 ].replace( runescape, funescape ),
                                rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
                                context
                            ) ) ) {

                                // If seed is empty or no tokens remain, we can return early
                                tokens.splice( i, 1 );
                                selector = seed.length && toSelector( tokens );
                                if ( !selector ) {
                                    push.apply( results, seed );
                                    return results;
                                }

                                break;
                            }
                        }
                    }
                }

                // Compile and execute a filtering function if one is not provided
                // Provide `match` to avoid retokenization if we modified the selector above
                ( compiled || compile( selector, match ) )(
                    seed,
                    context,
                    !documentIsHTML,
                    results,
                    !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
                );
                return results;
            };

// One-time assignments

// Sort stability
            support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;

// Support: Chrome 14-35+
// Always assume duplicates if they aren't passed to the comparison function
            support.detectDuplicates = !!hasDuplicate;

// Initialize against the default document
            setDocument();

// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
            support.sortDetached = assert( function( el ) {

                // Should return 1, but returns 4 (following)
                return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
            } );

// Support: IE<8
// Prevent attribute/property "interpolation"
// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
            if ( !assert( function( el ) {
                el.innerHTML = "<a href='#'></a>";
                return el.firstChild.getAttribute( "href" ) === "#";
            } ) ) {
                addHandle( "type|href|height|width", function( elem, name, isXML ) {
                    if ( !isXML ) {
                        return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
                    }
                } );
            }

// Support: IE<9
// Use defaultValue in place of getAttribute("value")
            if ( !support.attributes || !assert( function( el ) {
                el.innerHTML = "<input/>";
                el.firstChild.setAttribute( "value", "" );
                return el.firstChild.getAttribute( "value" ) === "";
            } ) ) {
                addHandle( "value", function( elem, _name, isXML ) {
                    if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
                        return elem.defaultValue;
                    }
                } );
            }

// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
            if ( !assert( function( el ) {
                return el.getAttribute( "disabled" ) == null;
            } ) ) {
                addHandle( booleans, function( elem, name, isXML ) {
                    var val;
                    if ( !isXML ) {
                        return elem[ name ] === true ? name.toLowerCase() :
                            ( val = elem.getAttributeNode( name ) ) && val.specified ?
                                val.value :
                                null;
                    }
                } );
            }

            return Sizzle;

        } )( window );



    jQuery.find = Sizzle;
    jQuery.expr = Sizzle.selectors;

// Deprecated
    jQuery.expr[ ":" ] = jQuery.expr.pseudos;
    jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
    jQuery.text = Sizzle.getText;
    jQuery.isXMLDoc = Sizzle.isXML;
    jQuery.contains = Sizzle.contains;
    jQuery.escapeSelector = Sizzle.escape;




    var dir = function( elem, dir, until ) {
        var matched = [],
            truncate = until !== undefined;

        while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
            if ( elem.nodeType === 1 ) {
                if ( truncate && jQuery( elem ).is( until ) ) {
                    break;
                }
                matched.push( elem );
            }
        }
        return matched;
    };


    var siblings = function( n, elem ) {
        var matched = [];

        for ( ; n; n = n.nextSibling ) {
            if ( n.nodeType === 1 && n !== elem ) {
                matched.push( n );
            }
        }

        return matched;
    };


    var rneedsContext = jQuery.expr.match.needsContext;



    function nodeName( elem, name ) {

        return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();

    }
    var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );



// Implement the identical functionality for filter and not
    function winnow( elements, qualifier, not ) {
        if ( isFunction( qualifier ) ) {
            return jQuery.grep( elements, function( elem, i ) {
                return !!qualifier.call( elem, i, elem ) !== not;
            } );
        }

        // Single element
        if ( qualifier.nodeType ) {
            return jQuery.grep( elements, function( elem ) {
                return ( elem === qualifier ) !== not;
            } );
        }

        // Arraylike of elements (jQuery, arguments, Array)
        if ( typeof qualifier !== "string" ) {
            return jQuery.grep( elements, function( elem ) {
                return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
            } );
        }

        // Filtered directly for both simple and complex selectors
        return jQuery.filter( qualifier, elements, not );
    }

    jQuery.filter = function( expr, elems, not ) {
        var elem = elems[ 0 ];

        if ( not ) {
            expr = ":not(" + expr + ")";
        }

        if ( elems.length === 1 && elem.nodeType === 1 ) {
            return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
        }

        return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
            return elem.nodeType === 1;
        } ) );
    };

    jQuery.fn.extend( {
        find: function( selector ) {
            var i, ret,
                len = this.length,
                self = this;

            if ( typeof selector !== "string" ) {
                return this.pushStack( jQuery( selector ).filter( function() {
                    for ( i = 0; i < len; i++ ) {
                        if ( jQuery.contains( self[ i ], this ) ) {
                            return true;
                        }
                    }
                } ) );
            }

            ret = this.pushStack( [] );

            for ( i = 0; i < len; i++ ) {
                jQuery.find( selector, self[ i ], ret );
            }

            return len > 1 ? jQuery.uniqueSort( ret ) : ret;
        },
        filter: function( selector ) {
            return this.pushStack( winnow( this, selector || [], false ) );
        },
        not: function( selector ) {
            return this.pushStack( winnow( this, selector || [], true ) );
        },
        is: function( selector ) {
            return !!winnow(
                this,

                // If this is a positional/relative selector, check membership in the returned set
                // so $("p:first").is("p:last") won't return true for a doc with two "p".
                typeof selector === "string" && rneedsContext.test( selector ) ?
                    jQuery( selector ) :
                    selector || [],
                false
            ).length;
        }
    } );


// Initialize a jQuery object


// A central reference to the root jQuery(document)
    var rootjQuery,

        // A simple way to check for HTML strings
        // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
        // Strict HTML recognition (#11290: must start with <)
        // Shortcut simple #id case for speed
        rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,

        init = jQuery.fn.init = function( selector, context, root ) {
            var match, elem;

            // HANDLE: $(""), $(null), $(undefined), $(false)
            if ( !selector ) {
                return this;
            }

            // Method init() accepts an alternate rootjQuery
            // so migrate can support jQuery.sub (gh-2101)
            root = root || rootjQuery;

            // Handle HTML strings
            if ( typeof selector === "string" ) {
                if ( selector[ 0 ] === "<" &&
                    selector[ selector.length - 1 ] === ">" &&
                    selector.length >= 3 ) {

                    // Assume that strings that start and end with <> are HTML and skip the regex check
                    match = [ null, selector, null ];

                } else {
                    match = rquickExpr.exec( selector );
                }

                // Match html or make sure no context is specified for #id
                if ( match && ( match[ 1 ] || !context ) ) {

                    // HANDLE: $(html) -> $(array)
                    if ( match[ 1 ] ) {
                        context = context instanceof jQuery ? context[ 0 ] : context;

                        // Option to run scripts is true for back-compat
                        // Intentionally let the error be thrown if parseHTML is not present
                        jQuery.merge( this, jQuery.parseHTML(
                            match[ 1 ],
                            context && context.nodeType ? context.ownerDocument || context : document,
                            true
                        ) );

                        // HANDLE: $(html, props)
                        if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
                            for ( match in context ) {

                                // Properties of context are called as methods if possible
                                if ( isFunction( this[ match ] ) ) {
                                    this[ match ]( context[ match ] );

                                    // ...and otherwise set as attributes
                                } else {
                                    this.attr( match, context[ match ] );
                                }
                            }
                        }

                        return this;

                        // HANDLE: $(#id)
                    } else {
                        elem = document.getElementById( match[ 2 ] );

                        if ( elem ) {

                            // Inject the element directly into the jQuery object
                            this[ 0 ] = elem;
                            this.length = 1;
                        }
                        return this;
                    }

                    // HANDLE: $(expr, $(...))
                } else if ( !context || context.jquery ) {
                    return ( context || root ).find( selector );

                    // HANDLE: $(expr, context)
                    // (which is just equivalent to: $(context).find(expr)
                } else {
                    return this.constructor( context ).find( selector );
                }

                // HANDLE: $(DOMElement)
            } else if ( selector.nodeType ) {
                this[ 0 ] = selector;
                this.length = 1;
                return this;

                // HANDLE: $(function)
                // Shortcut for document ready
            } else if ( isFunction( selector ) ) {
                return root.ready !== undefined ?
                    root.ready( selector ) :

                    // Execute immediately if ready is not present
                    selector( jQuery );
            }

            return jQuery.makeArray( selector, this );
        };

// Give the init function the jQuery prototype for later instantiation
    init.prototype = jQuery.fn;

// Initialize central reference
    rootjQuery = jQuery( document );


    var rparentsprev = /^(?:parents|prev(?:Until|All))/,

        // Methods guaranteed to produce a unique set when starting from a unique set
        guaranteedUnique = {
            children: true,
            contents: true,
            next: true,
            prev: true
        };

    jQuery.fn.extend( {
        has: function( target ) {
            var targets = jQuery( target, this ),
                l = targets.length;

            return this.filter( function() {
                var i = 0;
                for ( ; i < l; i++ ) {
                    if ( jQuery.contains( this, targets[ i ] ) ) {
                        return true;
                    }
                }
            } );
        },

        closest: function( selectors, context ) {
            var cur,
                i = 0,
                l = this.length,
                matched = [],
                targets = typeof selectors !== "string" && jQuery( selectors );

            // Positional selectors never match, since there's no _selection_ context
            if ( !rneedsContext.test( selectors ) ) {
                for ( ; i < l; i++ ) {
                    for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {

                        // Always skip document fragments
                        if ( cur.nodeType < 11 && ( targets ?
                            targets.index( cur ) > -1 :

                            // Don't pass non-elements to Sizzle
                            cur.nodeType === 1 &&
                            jQuery.find.matchesSelector( cur, selectors ) ) ) {

                            matched.push( cur );
                            break;
                        }
                    }
                }
            }

            return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
        },

        // Determine the position of an element within the set
        index: function( elem ) {

            // No argument, return index in parent
            if ( !elem ) {
                return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
            }

            // Index in selector
            if ( typeof elem === "string" ) {
                return indexOf.call( jQuery( elem ), this[ 0 ] );
            }

            // Locate the position of the desired element
            return indexOf.call( this,

                // If it receives a jQuery object, the first element is used
                elem.jquery ? elem[ 0 ] : elem
            );
        },

        add: function( selector, context ) {
            return this.pushStack(
                jQuery.uniqueSort(
                    jQuery.merge( this.get(), jQuery( selector, context ) )
                )
            );
        },

        addBack: function( selector ) {
            return this.add( selector == null ?
                this.prevObject : this.prevObject.filter( selector )
            );
        }
    } );

    function sibling( cur, dir ) {
        while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
        return cur;
    }

    jQuery.each( {
        parent: function( elem ) {
            var parent = elem.parentNode;
            return parent && parent.nodeType !== 11 ? parent : null;
        },
        parents: function( elem ) {
            return dir( elem, "parentNode" );
        },
        parentsUntil: function( elem, _i, until ) {
            return dir( elem, "parentNode", until );
        },
        next: function( elem ) {
            return sibling( elem, "nextSibling" );
        },
        prev: function( elem ) {
            return sibling( elem, "previousSibling" );
        },
        nextAll: function( elem ) {
            return dir( elem, "nextSibling" );
        },
        prevAll: function( elem ) {
            return dir( elem, "previousSibling" );
        },
        nextUntil: function( elem, _i, until ) {
            return dir( elem, "nextSibling", until );
        },
        prevUntil: function( elem, _i, until ) {
            return dir( elem, "previousSibling", until );
        },
        siblings: function( elem ) {
            return siblings( ( elem.parentNode || {} ).firstChild, elem );
        },
        children: function( elem ) {
            return siblings( elem.firstChild );
        },
        contents: function( elem ) {
            if ( elem.contentDocument != null &&

                // Support: IE 11+
                // <object> elements with no `data` attribute has an object
                // `contentDocument` with a `null` prototype.
                getProto( elem.contentDocument ) ) {

                return elem.contentDocument;
            }

            // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
            // Treat the template element as a regular one in browsers that
            // don't support it.
            if ( nodeName( elem, "template" ) ) {
                elem = elem.content || elem;
            }

            return jQuery.merge( [], elem.childNodes );
        }
    }, function( name, fn ) {
        jQuery.fn[ name ] = function( until, selector ) {
            var matched = jQuery.map( this, fn, until );

            if ( name.slice( -5 ) !== "Until" ) {
                selector = until;
            }

            if ( selector && typeof selector === "string" ) {
                matched = jQuery.filter( selector, matched );
            }

            if ( this.length > 1 ) {

                // Remove duplicates
                if ( !guaranteedUnique[ name ] ) {
                    jQuery.uniqueSort( matched );
                }

                // Reverse order for parents* and prev-derivatives
                if ( rparentsprev.test( name ) ) {
                    matched.reverse();
                }
            }

            return this.pushStack( matched );
        };
    } );
    var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );



// Convert String-formatted options into Object-formatted ones
    function createOptions( options ) {
        var object = {};
        jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
            object[ flag ] = true;
        } );
        return object;
    }

    /*
 * Create a callback list using the following parameters:
 *
 *	options: an optional list of space-separated options that will change how
 *			the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *	once:			will ensure the callback list can only be fired once (like a Deferred)
 *
 *	memory:			will keep track of previous values and will call any callback added
 *					after the list has been fired right away with the latest "memorized"
 *					values (like a Deferred)
 *
 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
 *
 *	stopOnFalse:	interrupt callings when a callback returns false
 *
 */
    jQuery.Callbacks = function( options ) {

        // Convert options from String-formatted to Object-formatted if needed
        // (we check in cache first)
        options = typeof options === "string" ?
            createOptions( options ) :
            jQuery.extend( {}, options );

        var // Flag to know if list is currently firing
            firing,

            // Last fire value for non-forgettable lists
            memory,

            // Flag to know if list was already fired
            fired,

            // Flag to prevent firing
            locked,

            // Actual callback list
            list = [],

            // Queue of execution data for repeatable lists
            queue = [],

            // Index of currently firing callback (modified by add/remove as needed)
            firingIndex = -1,

            // Fire callbacks
            fire = function() {

                // Enforce single-firing
                locked = locked || options.once;

                // Execute callbacks for all pending executions,
                // respecting firingIndex overrides and runtime changes
                fired = firing = true;
                for ( ; queue.length; firingIndex = -1 ) {
                    memory = queue.shift();
                    while ( ++firingIndex < list.length ) {

                        // Run callback and check for early termination
                        if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                            options.stopOnFalse ) {

                            // Jump to end and forget the data so .add doesn't re-fire
                            firingIndex = list.length;
                            memory = false;
                        }
                    }
                }

                // Forget the data if we're done with it
                if ( !options.memory ) {
                    memory = false;
                }

                firing = false;

                // Clean up if we're done firing for good
                if ( locked ) {

                    // Keep an empty list if we have data for future add calls
                    if ( memory ) {
                        list = [];

                        // Otherwise, this object is spent
                    } else {
                        list = "";
                    }
                }
            },

            // Actual Callbacks object
            self = {

                // Add a callback or a collection of callbacks to the list
                add: function() {
                    if ( list ) {

                        // If we have memory from a past run, we should fire after adding
                        if ( memory && !firing ) {
                            firingIndex = list.length - 1;
                            queue.push( memory );
                        }

                        ( function add( args ) {
                            jQuery.each( args, function( _, arg ) {
                                if ( isFunction( arg ) ) {
                                    if ( !options.unique || !self.has( arg ) ) {
                                        list.push( arg );
                                    }
                                } else if ( arg && arg.length && toType( arg ) !== "string" ) {

                                    // Inspect recursively
                                    add( arg );
                                }
                            } );
                        } )( arguments );

                        if ( memory && !firing ) {
                            fire();
                        }
                    }
                    return this;
                },

                // Remove a callback from the list
                remove: function() {
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            list.splice( index, 1 );

                            // Handle firing indexes
                            if ( index <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    } );
                    return this;
                },

                // Check if a given callback is in the list.
                // If no argument is given, return whether or not list has callbacks attached.
                has: function( fn ) {
                    return fn ?
                        jQuery.inArray( fn, list ) > -1 :
                        list.length > 0;
                },

                // Remove all callbacks from the list
                empty: function() {
                    if ( list ) {
                        list = [];
                    }
                    return this;
                },

                // Disable .fire and .add
                // Abort any current/pending executions
                // Clear all callbacks and values
                disable: function() {
                    locked = queue = [];
                    list = memory = "";
                    return this;
                },
                disabled: function() {
                    return !list;
                },

                // Disable .fire
                // Also disable .add unless we have memory (since it would have no effect)
                // Abort any pending executions
                lock: function() {
                    locked = queue = [];
                    if ( !memory && !firing ) {
                        list = memory = "";
                    }
                    return this;
                },
                locked: function() {
                    return !!locked;
                },

                // Call all callbacks with the given context and arguments
                fireWith: function( context, args ) {
                    if ( !locked ) {
                        args = args || [];
                        args = [ context, args.slice ? args.slice() : args ];
                        queue.push( args );
                        if ( !firing ) {
                            fire();
                        }
                    }
                    return this;
                },

                // Call all the callbacks with the given arguments
                fire: function() {
                    self.fireWith( this, arguments );
                    return this;
                },

                // To know if the callbacks have already been called at least once
                fired: function() {
                    return !!fired;
                }
            };

        return self;
    };


    function Identity( v ) {
        return v;
    }
    function Thrower( ex ) {
        throw ex;
    }

    function adoptValue( value, resolve, reject, noValue ) {
        var method;

        try {

            // Check for promise aspect first to privilege synchronous behavior
            if ( value && isFunction( ( method = value.promise ) ) ) {
                method.call( value ).done( resolve ).fail( reject );

                // Other thenables
            } else if ( value && isFunction( ( method = value.then ) ) ) {
                method.call( value, resolve, reject );

                // Other non-thenables
            } else {

                // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
                // * false: [ value ].slice( 0 ) => resolve( value )
                // * true: [ value ].slice( 1 ) => resolve()
                resolve.apply( undefined, [ value ].slice( noValue ) );
            }

            // For Promises/A+, convert exceptions into rejections
            // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
            // Deferred#then to conditionally suppress rejection.
        } catch ( value ) {

            // Support: Android 4.0 only
            // Strict mode functions invoked without .call/.apply get global-object context
            reject.apply( undefined, [ value ] );
        }
    }

    jQuery.extend( {

        Deferred: function( func ) {
            var tuples = [

                    // action, add listener, callbacks,
                    // ... .then handlers, argument index, [final state]
                    [ "notify", "progress", jQuery.Callbacks( "memory" ),
                        jQuery.Callbacks( "memory" ), 2 ],
                    [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                        jQuery.Callbacks( "once memory" ), 0, "resolved" ],
                    [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                        jQuery.Callbacks( "once memory" ), 1, "rejected" ]
                ],
                state = "pending",
                promise = {
                    state: function() {
                        return state;
                    },
                    always: function() {
                        deferred.done( arguments ).fail( arguments );
                        return this;
                    },
                    "catch": function( fn ) {
                        return promise.then( null, fn );
                    },

                    // Keep pipe for back-compat
                    pipe: function( /* fnDone, fnFail, fnProgress */ ) {
                        var fns = arguments;

                        return jQuery.Deferred( function( newDefer ) {
                            jQuery.each( tuples, function( _i, tuple ) {

                                // Map tuples (progress, done, fail) to arguments (done, fail, progress)
                                var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];

                                // deferred.progress(function() { bind to newDefer or newDefer.notify })
                                // deferred.done(function() { bind to newDefer or newDefer.resolve })
                                // deferred.fail(function() { bind to newDefer or newDefer.reject })
                                deferred[ tuple[ 1 ] ]( function() {
                                    var returned = fn && fn.apply( this, arguments );
                                    if ( returned && isFunction( returned.promise ) ) {
                                        returned.promise()
                                            .progress( newDefer.notify )
                                            .done( newDefer.resolve )
                                            .fail( newDefer.reject );
                                    } else {
                                        newDefer[ tuple[ 0 ] + "With" ](
                                            this,
                                            fn ? [ returned ] : arguments
                                        );
                                    }
                                } );
                            } );
                            fns = null;
                        } ).promise();
                    },
                    then: function( onFulfilled, onRejected, onProgress ) {
                        var maxDepth = 0;
                        function resolve( depth, deferred, handler, special ) {
                            return function() {
                                var that = this,
                                    args = arguments,
                                    mightThrow = function() {
                                        var returned, then;

                                        // Support: Promises/A+ section 2.3.3.3.3
                                        // https://promisesaplus.com/#point-59
                                        // Ignore double-resolution attempts
                                        if ( depth < maxDepth ) {
                                            return;
                                        }

                                        returned = handler.apply( that, args );

                                        // Support: Promises/A+ section 2.3.1
                                        // https://promisesaplus.com/#point-48
                                        if ( returned === deferred.promise() ) {
                                            throw new TypeError( "Thenable self-resolution" );
                                        }

                                        // Support: Promises/A+ sections 2.3.3.1, 3.5
                                        // https://promisesaplus.com/#point-54
                                        // https://promisesaplus.com/#point-75
                                        // Retrieve `then` only once
                                        then = returned &&

                                            // Support: Promises/A+ section 2.3.4
                                            // https://promisesaplus.com/#point-64
                                            // Only check objects and functions for thenability
                                            ( typeof returned === "object" ||
                                                typeof returned === "function" ) &&
                                            returned.then;

                                        // Handle a returned thenable
                                        if ( isFunction( then ) ) {

                                            // Special processors (notify) just wait for resolution
                                            if ( special ) {
                                                then.call(
                                                    returned,
                                                    resolve( maxDepth, deferred, Identity, special ),
                                                    resolve( maxDepth, deferred, Thrower, special )
                                                );

                                                // Normal processors (resolve) also hook into progress
                                            } else {

                                                // ...and disregard older resolution values
                                                maxDepth++;

                                                then.call(
                                                    returned,
                                                    resolve( maxDepth, deferred, Identity, special ),
                                                    resolve( maxDepth, deferred, Thrower, special ),
                                                    resolve( maxDepth, deferred, Identity,
                                                        deferred.notifyWith )
                                                );
                                            }

                                            // Handle all other returned values
                                        } else {

                                            // Only substitute handlers pass on context
                                            // and multiple values (non-spec behavior)
                                            if ( handler !== Identity ) {
                                                that = undefined;
                                                args = [ returned ];
                                            }

                                            // Process the value(s)
                                            // Default process is resolve
                                            ( special || deferred.resolveWith )( that, args );
                                        }
                                    },

                                    // Only normal processors (resolve) catch and reject exceptions
                                    process = special ?
                                        mightThrow :
                                        function() {
                                            try {
                                                mightThrow();
                                            } catch ( e ) {

                                                if ( jQuery.Deferred.exceptionHook ) {
                                                    jQuery.Deferred.exceptionHook( e,
                                                        process.stackTrace );
                                                }

                                                // Support: Promises/A+ section 2.3.3.3.4.1
                                                // https://promisesaplus.com/#point-61
                                                // Ignore post-resolution exceptions
                                                if ( depth + 1 >= maxDepth ) {

                                                    // Only substitute handlers pass on context
                                                    // and multiple values (non-spec behavior)
                                                    if ( handler !== Thrower ) {
                                                        that = undefined;
                                                        args = [ e ];
                                                    }

                                                    deferred.rejectWith( that, args );
                                                }
                                            }
                                        };

                                // Support: Promises/A+ section 2.3.3.3.1
                                // https://promisesaplus.com/#point-57
                                // Re-resolve promises immediately to dodge false rejection from
                                // subsequent errors
                                if ( depth ) {
                                    process();
                                } else {

                                    // Call an optional hook to record the stack, in case of exception
                                    // since it's otherwise lost when execution goes async
                                    if ( jQuery.Deferred.getStackHook ) {
                                        process.stackTrace = jQuery.Deferred.getStackHook();
                                    }
                                    window.setTimeout( process );
                                }
                            };
                        }

                        return jQuery.Deferred( function( newDefer ) {

                            // progress_handlers.add( ... )
                            tuples[ 0 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    isFunction( onProgress ) ?
                                        onProgress :
                                        Identity,
                                    newDefer.notifyWith
                                )
                            );

                            // fulfilled_handlers.add( ... )
                            tuples[ 1 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    isFunction( onFulfilled ) ?
                                        onFulfilled :
                                        Identity
                                )
                            );

                            // rejected_handlers.add( ... )
                            tuples[ 2 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    isFunction( onRejected ) ?
                                        onRejected :
                                        Thrower
                                )
                            );
                        } ).promise();
                    },

                    // Get a promise for this deferred
                    // If obj is provided, the promise aspect is added to the object
                    promise: function( obj ) {
                        return obj != null ? jQuery.extend( obj, promise ) : promise;
                    }
                },
                deferred = {};

            // Add list-specific methods
            jQuery.each( tuples, function( i, tuple ) {
                var list = tuple[ 2 ],
                    stateString = tuple[ 5 ];

                // promise.progress = list.add
                // promise.done = list.add
                // promise.fail = list.add
                promise[ tuple[ 1 ] ] = list.add;

                // Handle state
                if ( stateString ) {
                    list.add(
                        function() {

                            // state = "resolved" (i.e., fulfilled)
                            // state = "rejected"
                            state = stateString;
                        },

                        // rejected_callbacks.disable
                        // fulfilled_callbacks.disable
                        tuples[ 3 - i ][ 2 ].disable,

                        // rejected_handlers.disable
                        // fulfilled_handlers.disable
                        tuples[ 3 - i ][ 3 ].disable,

                        // progress_callbacks.lock
                        tuples[ 0 ][ 2 ].lock,

                        // progress_handlers.lock
                        tuples[ 0 ][ 3 ].lock
                    );
                }

                // progress_handlers.fire
                // fulfilled_handlers.fire
                // rejected_handlers.fire
                list.add( tuple[ 3 ].fire );

                // deferred.notify = function() { deferred.notifyWith(...) }
                // deferred.resolve = function() { deferred.resolveWith(...) }
                // deferred.reject = function() { deferred.rejectWith(...) }
                deferred[ tuple[ 0 ] ] = function() {
                    deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                    return this;
                };

                // deferred.notifyWith = list.fireWith
                // deferred.resolveWith = list.fireWith
                // deferred.rejectWith = list.fireWith
                deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
            } );

            // Make the deferred a promise
            promise.promise( deferred );

            // Call given func if any
            if ( func ) {
                func.call( deferred, deferred );
            }

            // All done!
            return deferred;
        },

        // Deferred helper
        when: function( singleValue ) {
            var

                // count of uncompleted subordinates
                remaining = arguments.length,

                // count of unprocessed arguments
                i = remaining,

                // subordinate fulfillment data
                resolveContexts = Array( i ),
                resolveValues = slice.call( arguments ),

                // the primary Deferred
                primary = jQuery.Deferred(),

                // subordinate callback factory
                updateFunc = function( i ) {
                    return function( value ) {
                        resolveContexts[ i ] = this;
                        resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                        if ( !( --remaining ) ) {
                            primary.resolveWith( resolveContexts, resolveValues );
                        }
                    };
                };

            // Single- and empty arguments are adopted like Promise.resolve
            if ( remaining <= 1 ) {
                adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
                    !remaining );

                // Use .then() to unwrap secondary thenables (cf. gh-3000)
                if ( primary.state() === "pending" ||
                    isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

                    return primary.then();
                }
            }

            // Multiple arguments are aggregated like Promise.all array elements
            while ( i-- ) {
                adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
            }

            return primary.promise();
        }
    } );


// These usually indicate a programmer mistake during development,
// warn about them ASAP rather than swallowing them by default.
    var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;

    jQuery.Deferred.exceptionHook = function( error, stack ) {

        // Support: IE 8 - 9 only
        // Console exists when dev tools are open, which can happen at any time
        if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
            window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
        }
    };




    jQuery.readyException = function( error ) {
        window.setTimeout( function() {
            throw error;
        } );
    };




// The deferred used on DOM ready
    var readyList = jQuery.Deferred();

    jQuery.fn.ready = function( fn ) {

        readyList
            .then( fn )

            // Wrap jQuery.readyException in a function so that the lookup
            // happens at the time of error handling instead of callback
            // registration.
            .catch( function( error ) {
                jQuery.readyException( error );
            } );

        return this;
    };

    jQuery.extend( {

        // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,

        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,

        // Handle when the DOM is ready
        ready: function( wait ) {

            // Abort if there are pending holds or we're already ready
            if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
                return;
            }

            // Remember that the DOM is ready
            jQuery.isReady = true;

            // If a normal DOM Ready event fired, decrement, and wait if need be
            if ( wait !== true && --jQuery.readyWait > 0 ) {
                return;
            }

            // If there are functions bound, to execute
            readyList.resolveWith( document, [ jQuery ] );
        }
    } );

    jQuery.ready.then = readyList.then;

// The ready event handler and self cleanup method
    function completed() {
        document.removeEventListener( "DOMContentLoaded", completed );
        window.removeEventListener( "load", completed );
        jQuery.ready();
    }

// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
    if ( document.readyState === "complete" ||
        ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {

        // Handle it asynchronously to allow scripts the opportunity to delay ready
        window.setTimeout( jQuery.ready );

    } else {

        // Use the handy event callback
        document.addEventListener( "DOMContentLoaded", completed );

        // A fallback to window.onload, that will always work
        window.addEventListener( "load", completed );
    }




// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
    var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
        var i = 0,
            len = elems.length,
            bulk = key == null;

        // Sets many values
        if ( toType( key ) === "object" ) {
            chainable = true;
            for ( i in key ) {
                access( elems, fn, i, key[ i ], true, emptyGet, raw );
            }

            // Sets one value
        } else if ( value !== undefined ) {
            chainable = true;

            if ( !isFunction( value ) ) {
                raw = true;
            }

            if ( bulk ) {

                // Bulk operations run against the entire set
                if ( raw ) {
                    fn.call( elems, value );
                    fn = null;

                    // ...except when executing function values
                } else {
                    bulk = fn;
                    fn = function( elem, _key, value ) {
                        return bulk.call( jQuery( elem ), value );
                    };
                }
            }

            if ( fn ) {
                for ( ; i < len; i++ ) {
                    fn(
                        elems[ i ], key, raw ?
                            value :
                            value.call( elems[ i ], i, fn( elems[ i ], key ) )
                    );
                }
            }
        }

        if ( chainable ) {
            return elems;
        }

        // Gets
        if ( bulk ) {
            return fn.call( elems );
        }

        return len ? fn( elems[ 0 ], key ) : emptyGet;
    };


// Matches dashed string for camelizing
    var rmsPrefix = /^-ms-/,
        rdashAlpha = /-([a-z])/g;

// Used by camelCase as callback to replace()
    function fcamelCase( _all, letter ) {
        return letter.toUpperCase();
    }

// Convert dashed to camelCase; used by the css and data modules
// Support: IE <=9 - 11, Edge 12 - 15
// Microsoft forgot to hump their vendor prefix (#9572)
    function camelCase( string ) {
        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
    }
    var acceptData = function( owner ) {

        // Accepts only:
        //  - Node
        //    - Node.ELEMENT_NODE
        //    - Node.DOCUMENT_NODE
        //  - Object
        //    - Any
        return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
    };




    function Data() {
        this.expando = jQuery.expando + Data.uid++;
    }

    Data.uid = 1;

    Data.prototype = {

        cache: function( owner ) {

            // Check if the owner object already has a cache
            var value = owner[ this.expando ];

            // If not, create one
            if ( !value ) {
                value = {};

                // We can accept data for non-element nodes in modern browsers,
                // but we should not, see #8335.
                // Always return an empty object.
                if ( acceptData( owner ) ) {

                    // If it is a node unlikely to be stringify-ed or looped over
                    // use plain assignment
                    if ( owner.nodeType ) {
                        owner[ this.expando ] = value;

                        // Otherwise secure it in a non-enumerable property
                        // configurable must be true to allow the property to be
                        // deleted when data is removed
                    } else {
                        Object.defineProperty( owner, this.expando, {
                            value: value,
                            configurable: true
                        } );
                    }
                }
            }

            return value;
        },
        set: function( owner, data, value ) {
            var prop,
                cache = this.cache( owner );

            // Handle: [ owner, key, value ] args
            // Always use camelCase key (gh-2257)
            if ( typeof data === "string" ) {
                cache[ camelCase( data ) ] = value;

                // Handle: [ owner, { properties } ] args
            } else {

                // Copy the properties one-by-one to the cache object
                for ( prop in data ) {
                    cache[ camelCase( prop ) ] = data[ prop ];
                }
            }
            return cache;
        },
        get: function( owner, key ) {
            return key === undefined ?
                this.cache( owner ) :

                // Always use camelCase key (gh-2257)
                owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
        },
        access: function( owner, key, value ) {

            // In cases where either:
            //
            //   1. No key was specified
            //   2. A string key was specified, but no value provided
            //
            // Take the "read" path and allow the get method to determine
            // which value to return, respectively either:
            //
            //   1. The entire cache object
            //   2. The data stored at the key
            //
            if ( key === undefined ||
                ( ( key && typeof key === "string" ) && value === undefined ) ) {

                return this.get( owner, key );
            }

            // When the key is not a string, or both a key and value
            // are specified, set or extend (existing objects) with either:
            //
            //   1. An object of properties
            //   2. A key and value
            //
            this.set( owner, key, value );

            // Since the "set" path can have two possible entry points
            // return the expected data based on which path was taken[*]
            return value !== undefined ? value : key;
        },
        remove: function( owner, key ) {
            var i,
                cache = owner[ this.expando ];

            if ( cache === undefined ) {
                return;
            }

            if ( key !== undefined ) {

                // Support array or space separated string of keys
                if ( Array.isArray( key ) ) {

                    // If key is an array of keys...
                    // We always set camelCase keys, so remove that.
                    key = key.map( camelCase );
                } else {
                    key = camelCase( key );

                    // If a key with the spaces exists, use it.
                    // Otherwise, create an array by matching non-whitespace
                    key = key in cache ?
                        [ key ] :
                        ( key.match( rnothtmlwhite ) || [] );
                }

                i = key.length;

                while ( i-- ) {
                    delete cache[ key[ i ] ];
                }
            }

            // Remove the expando if there's no more data
            if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

                // Support: Chrome <=35 - 45
                // Webkit & Blink performance suffers when deleting properties
                // from DOM nodes, so set to undefined instead
                // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
                if ( owner.nodeType ) {
                    owner[ this.expando ] = undefined;
                } else {
                    delete owner[ this.expando ];
                }
            }
        },
        hasData: function( owner ) {
            var cache = owner[ this.expando ];
            return cache !== undefined && !jQuery.isEmptyObject( cache );
        }
    };
    var dataPriv = new Data();

    var dataUser = new Data();



//	Implementation Summary
//
//	1. Enforce API surface and semantic compatibility with 1.9.x branch
//	2. Improve the module's maintainability by reducing the storage
//		paths to a single mechanism.
//	3. Use the same single mechanism to support "private" and "user" data.
//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
//	5. Avoid exposing implementation details on user objects (eg. expando properties)
//	6. Provide a clear path for implementation upgrade to WeakMap in 2014

    var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
        rmultiDash = /[A-Z]/g;

    function getData( data ) {
        if ( data === "true" ) {
            return true;
        }

        if ( data === "false" ) {
            return false;
        }

        if ( data === "null" ) {
            return null;
        }

        // Only convert to a number if it doesn't change the string
        if ( data === +data + "" ) {
            return +data;
        }

        if ( rbrace.test( data ) ) {
            return JSON.parse( data );
        }

        return data;
    }

    function dataAttr( elem, key, data ) {
        var name;

        // If nothing was found internally, try to fetch any
        // data from the HTML5 data-* attribute
        if ( data === undefined && elem.nodeType === 1 ) {
            name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
            data = elem.getAttribute( name );

            if ( typeof data === "string" ) {
                try {
                    data = getData( data );
                } catch ( e ) {}

                // Make sure we set the data so it isn't changed later
                dataUser.set( elem, key, data );
            } else {
                data = undefined;
            }
        }
        return data;
    }

    jQuery.extend( {
        hasData: function( elem ) {
            return dataUser.hasData( elem ) || dataPriv.hasData( elem );
        },

        data: function( elem, name, data ) {
            return dataUser.access( elem, name, data );
        },

        removeData: function( elem, name ) {
            dataUser.remove( elem, name );
        },

        // TODO: Now that all calls to _data and _removeData have been replaced
        // with direct calls to dataPriv methods, these can be deprecated.
        _data: function( elem, name, data ) {
            return dataPriv.access( elem, name, data );
        },

        _removeData: function( elem, name ) {
            dataPriv.remove( elem, name );
        }
    } );

    jQuery.fn.extend( {
        data: function( key, value ) {
            var i, name, data,
                elem = this[ 0 ],
                attrs = elem && elem.attributes;

            // Gets all values
            if ( key === undefined ) {
                if ( this.length ) {
                    data = dataUser.get( elem );

                    if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
                        i = attrs.length;
                        while ( i-- ) {

                            // Support: IE 11 only
                            // The attrs elements can be null (#14894)
                            if ( attrs[ i ] ) {
                                name = attrs[ i ].name;
                                if ( name.indexOf( "data-" ) === 0 ) {
                                    name = camelCase( name.slice( 5 ) );
                                    dataAttr( elem, name, data[ name ] );
                                }
                            }
                        }
                        dataPriv.set( elem, "hasDataAttrs", true );
                    }
                }

                return data;
            }

            // Sets multiple values
            if ( typeof key === "object" ) {
                return this.each( function() {
                    dataUser.set( this, key );
                } );
            }

            return access( this, function( value ) {
                var data;

                // The calling jQuery object (element matches) is not empty
                // (and therefore has an element appears at this[ 0 ]) and the
                // `value` parameter was not undefined. An empty jQuery object
                // will result in `undefined` for elem = this[ 0 ] which will
                // throw an exception if an attempt to read a data cache is made.
                if ( elem && value === undefined ) {

                    // Attempt to get data from the cache
                    // The key will always be camelCased in Data
                    data = dataUser.get( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }

                    // Attempt to "discover" the data in
                    // HTML5 custom data-* attrs
                    data = dataAttr( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }

                    // We tried really hard, but the data doesn't exist.
                    return;
                }

                // Set the data...
                this.each( function() {

                    // We always store the camelCased key
                    dataUser.set( this, key, value );
                } );
            }, null, value, arguments.length > 1, null, true );
        },

        removeData: function( key ) {
            return this.each( function() {
                dataUser.remove( this, key );
            } );
        }
    } );


    jQuery.extend( {
        queue: function( elem, type, data ) {
            var queue;

            if ( elem ) {
                type = ( type || "fx" ) + "queue";
                queue = dataPriv.get( elem, type );

                // Speed up dequeue by getting out quickly if this is just a lookup
                if ( data ) {
                    if ( !queue || Array.isArray( data ) ) {
                        queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
                    } else {
                        queue.push( data );
                    }
                }
                return queue || [];
            }
        },

        dequeue: function( elem, type ) {
            type = type || "fx";

            var queue = jQuery.queue( elem, type ),
                startLength = queue.length,
                fn = queue.shift(),
                hooks = jQuery._queueHooks( elem, type ),
                next = function() {
                    jQuery.dequeue( elem, type );
                };

            // If the fx queue is dequeued, always remove the progress sentinel
            if ( fn === "inprogress" ) {
                fn = queue.shift();
                startLength--;
            }

            if ( fn ) {

                // Add a progress sentinel to prevent the fx queue from being
                // automatically dequeued
                if ( type === "fx" ) {
                    queue.unshift( "inprogress" );
                }

                // Clear up the last queue stop function
                delete hooks.stop;
                fn.call( elem, next, hooks );
            }

            if ( !startLength && hooks ) {
                hooks.empty.fire();
            }
        },

        // Not public - generate a queueHooks object, or return the current one
        _queueHooks: function( elem, type ) {
            var key = type + "queueHooks";
            return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
                empty: jQuery.Callbacks( "once memory" ).add( function() {
                    dataPriv.remove( elem, [ type + "queue", key ] );
                } )
            } );
        }
    } );

    jQuery.fn.extend( {
        queue: function( type, data ) {
            var setter = 2;

            if ( typeof type !== "string" ) {
                data = type;
                type = "fx";
                setter--;
            }

            if ( arguments.length < setter ) {
                return jQuery.queue( this[ 0 ], type );
            }

            return data === undefined ?
                this :
                this.each( function() {
                    var queue = jQuery.queue( this, type, data );

                    // Ensure a hooks for this queue
                    jQuery._queueHooks( this, type );

                    if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
                        jQuery.dequeue( this, type );
                    }
                } );
        },
        dequeue: function( type ) {
            return this.each( function() {
                jQuery.dequeue( this, type );
            } );
        },
        clearQueue: function( type ) {
            return this.queue( type || "fx", [] );
        },

        // Get a promise resolved when queues of a certain type
        // are emptied (fx is the type by default)
        promise: function( type, obj ) {
            var tmp,
                count = 1,
                defer = jQuery.Deferred(),
                elements = this,
                i = this.length,
                resolve = function() {
                    if ( !( --count ) ) {
                        defer.resolveWith( elements, [ elements ] );
                    }
                };

            if ( typeof type !== "string" ) {
                obj = type;
                type = undefined;
            }
            type = type || "fx";

            while ( i-- ) {
                tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
                if ( tmp && tmp.empty ) {
                    count++;
                    tmp.empty.add( resolve );
                }
            }
            resolve();
            return defer.promise( obj );
        }
    } );
    var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;

    var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );


    var cssExpand = [ "Top", "Right", "Bottom", "Left" ];

    var documentElement = document.documentElement;



    var isAttached = function( elem ) {
            return jQuery.contains( elem.ownerDocument, elem );
        },
        composed = { composed: true };

    // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
    // Check attachment across shadow DOM boundaries when possible (gh-3504)
    // Support: iOS 10.0-10.2 only
    // Early iOS 10 versions support `attachShadow` but not `getRootNode`,
    // leading to errors. We need to check for `getRootNode`.
    if ( documentElement.getRootNode ) {
        isAttached = function( elem ) {
            return jQuery.contains( elem.ownerDocument, elem ) ||
                elem.getRootNode( composed ) === elem.ownerDocument;
        };
    }
    var isHiddenWithinTree = function( elem, el ) {

        // isHiddenWithinTree might be called from jQuery#filter function;
        // in that case, element will be second argument
        elem = el || elem;

        // Inline style trumps all
        return elem.style.display === "none" ||
            elem.style.display === "" &&

            // Otherwise, check computed style
            // Support: Firefox <=43 - 45
            // Disconnected elements can have computed display: none, so first confirm that elem is
            // in the document.
            isAttached( elem ) &&

            jQuery.css( elem, "display" ) === "none";
    };



    function adjustCSS( elem, prop, valueParts, tween ) {
        var adjusted, scale,
            maxIterations = 20,
            currentValue = tween ?
                function() {
                    return tween.cur();
                } :
                function() {
                    return jQuery.css( elem, prop, "" );
                },
            initial = currentValue(),
            unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),

            // Starting value computation is required for potential unit mismatches
            initialInUnit = elem.nodeType &&
                ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
                rcssNum.exec( jQuery.css( elem, prop ) );

        if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {

            // Support: Firefox <=54
            // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
            initial = initial / 2;

            // Trust units reported by jQuery.css
            unit = unit || initialInUnit[ 3 ];

            // Iteratively approximate from a nonzero starting point
            initialInUnit = +initial || 1;

            while ( maxIterations-- ) {

                // Evaluate and update our best guess (doubling guesses that zero out).
                // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
                jQuery.style( elem, prop, initialInUnit + unit );
                if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
                    maxIterations = 0;
                }
                initialInUnit = initialInUnit / scale;

            }

            initialInUnit = initialInUnit * 2;
            jQuery.style( elem, prop, initialInUnit + unit );

            // Make sure we update the tween properties later on
            valueParts = valueParts || [];
        }

        if ( valueParts ) {
            initialInUnit = +initialInUnit || +initial || 0;

            // Apply relative offset (+=/-=) if specified
            adjusted = valueParts[ 1 ] ?
                initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
                +valueParts[ 2 ];
            if ( tween ) {
                tween.unit = unit;
                tween.start = initialInUnit;
                tween.end = adjusted;
            }
        }
        return adjusted;
    }


    var defaultDisplayMap = {};

    function getDefaultDisplay( elem ) {
        var temp,
            doc = elem.ownerDocument,
            nodeName = elem.nodeName,
            display = defaultDisplayMap[ nodeName ];

        if ( display ) {
            return display;
        }

        temp = doc.body.appendChild( doc.createElement( nodeName ) );
        display = jQuery.css( temp, "display" );

        temp.parentNode.removeChild( temp );

        if ( display === "none" ) {
            display = "block";
        }
        defaultDisplayMap[ nodeName ] = display;

        return display;
    }

    function showHide( elements, show ) {
        var display, elem,
            values = [],
            index = 0,
            length = elements.length;

        // Determine new display value for elements that need to change
        for ( ; index < length; index++ ) {
            elem = elements[ index ];
            if ( !elem.style ) {
                continue;
            }

            display = elem.style.display;
            if ( show ) {

                // Since we force visibility upon cascade-hidden elements, an immediate (and slow)
                // check is required in this first loop unless we have a nonempty display value (either
                // inline or about-to-be-restored)
                if ( display === "none" ) {
                    values[ index ] = dataPriv.get( elem, "display" ) || null;
                    if ( !values[ index ] ) {
                        elem.style.display = "";
                    }
                }
                if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
                    values[ index ] = getDefaultDisplay( elem );
                }
            } else {
                if ( display !== "none" ) {
                    values[ index ] = "none";

                    // Remember what we're overwriting
                    dataPriv.set( elem, "display", display );
                }
            }
        }

        // Set the display of the elements in a second loop to avoid constant reflow
        for ( index = 0; index < length; index++ ) {
            if ( values[ index ] != null ) {
                elements[ index ].style.display = values[ index ];
            }
        }

        return elements;
    }

    jQuery.fn.extend( {
        show: function() {
            return showHide( this, true );
        },
        hide: function() {
            return showHide( this );
        },
        toggle: function( state ) {
            if ( typeof state === "boolean" ) {
                return state ? this.show() : this.hide();
            }

            return this.each( function() {
                if ( isHiddenWithinTree( this ) ) {
                    jQuery( this ).show();
                } else {
                    jQuery( this ).hide();
                }
            } );
        }
    } );
    var rcheckableType = ( /^(?:checkbox|radio)$/i );

    var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );

    var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );



    ( function() {
        var fragment = document.createDocumentFragment(),
            div = fragment.appendChild( document.createElement( "div" ) ),
            input = document.createElement( "input" );

        // Support: Android 4.0 - 4.3 only
        // Check state lost if the name is set (#11217)
        // Support: Windows Web Apps (WWA)
        // `name` and `type` must use .setAttribute for WWA (#14901)
        input.setAttribute( "type", "radio" );
        input.setAttribute( "checked", "checked" );
        input.setAttribute( "name", "t" );

        div.appendChild( input );

        // Support: Android <=4.1 only
        // Older WebKit doesn't clone checked state correctly in fragments
        support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;

        // Support: IE <=11 only
        // Make sure textarea (and checkbox) defaultValue is properly cloned
        div.innerHTML = "<textarea>x</textarea>";
        support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;

        // Support: IE <=9 only
        // IE <=9 replaces <option> tags with their contents when inserted outside of
        // the select element.
        div.innerHTML = "<option></option>";
        support.option = !!div.lastChild;
    } )();


// We have to close these tags to support XHTML (#13200)
    var wrapMap = {

        // XHTML parsers do not magically insert elements in the
        // same way that tag soup parsers do. So we cannot shorten
        // this by omitting <tbody> or other required elements.
        thead: [ 1, "<table>", "</table>" ],
        col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
        tr: [ 2, "<table><tbody>", "</tbody></table>" ],
        td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],

        _default: [ 0, "", "" ]
    };

    wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
    wrapMap.th = wrapMap.td;

// Support: IE <=9 only
    if ( !support.option ) {
        wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
    }


    function getAll( context, tag ) {

        // Support: IE <=9 - 11 only
        // Use typeof to avoid zero-argument method invocation on host objects (#15151)
        var ret;

        if ( typeof context.getElementsByTagName !== "undefined" ) {
            ret = context.getElementsByTagName( tag || "*" );

        } else if ( typeof context.querySelectorAll !== "undefined" ) {
            ret = context.querySelectorAll( tag || "*" );

        } else {
            ret = [];
        }

        if ( tag === undefined || tag && nodeName( context, tag ) ) {
            return jQuery.merge( [ context ], ret );
        }

        return ret;
    }


// Mark scripts as having already been evaluated
    function setGlobalEval( elems, refElements ) {
        var i = 0,
            l = elems.length;

        for ( ; i < l; i++ ) {
            dataPriv.set(
                elems[ i ],
                "globalEval",
                !refElements || dataPriv.get( refElements[ i ], "globalEval" )
            );
        }
    }


    var rhtml = /<|&#?\w+;/;

    function buildFragment( elems, context, scripts, selection, ignored ) {
        var elem, tmp, tag, wrap, attached, j,
            fragment = context.createDocumentFragment(),
            nodes = [],
            i = 0,
            l = elems.length;

        for ( ; i < l; i++ ) {
            elem = elems[ i ];

            if ( elem || elem === 0 ) {

                // Add nodes directly
                if ( toType( elem ) === "object" ) {

                    // Support: Android <=4.0 only, PhantomJS 1 only
                    // push.apply(_, arraylike) throws on ancient WebKit
                    jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );

                    // Convert non-html into a text node
                } else if ( !rhtml.test( elem ) ) {
                    nodes.push( context.createTextNode( elem ) );

                    // Convert html into DOM nodes
                } else {
                    tmp = tmp || fragment.appendChild( context.createElement( "div" ) );

                    // Deserialize a standard representation
                    tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
                    wrap = wrapMap[ tag ] || wrapMap._default;
                    tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];

                    // Descend through wrappers to the right content
                    j = wrap[ 0 ];
                    while ( j-- ) {
                        tmp = tmp.lastChild;
                    }

                    // Support: Android <=4.0 only, PhantomJS 1 only
                    // push.apply(_, arraylike) throws on ancient WebKit
                    jQuery.merge( nodes, tmp.childNodes );

                    // Remember the top-level container
                    tmp = fragment.firstChild;

                    // Ensure the created nodes are orphaned (#12392)
                    tmp.textContent = "";
                }
            }
        }

        // Remove wrapper from fragment
        fragment.textContent = "";

        i = 0;
        while ( ( elem = nodes[ i++ ] ) ) {

            // Skip elements already in the context collection (trac-4087)
            if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
                if ( ignored ) {
                    ignored.push( elem );
                }
                continue;
            }

            attached = isAttached( elem );

            // Append to fragment
            tmp = getAll( fragment.appendChild( elem ), "script" );

            // Preserve script evaluation history
            if ( attached ) {
                setGlobalEval( tmp );
            }

            // Capture executables
            if ( scripts ) {
                j = 0;
                while ( ( elem = tmp[ j++ ] ) ) {
                    if ( rscriptType.test( elem.type || "" ) ) {
                        scripts.push( elem );
                    }
                }
            }
        }

        return fragment;
    }


    var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

    function returnTrue() {
        return true;
    }

    function returnFalse() {
        return false;
    }

// Support: IE <=9 - 11+
// focus() and blur() are asynchronous, except when they are no-op.
// So expect focus to be synchronous when the element is already active,
// and blur to be synchronous when the element is not already active.
// (focus and blur are always synchronous in other supported browsers,
// this just defines when we can count on it).
    function expectSync( elem, type ) {
        return ( elem === safeActiveElement() ) === ( type === "focus" );
    }

// Support: IE <=9 only
// Accessing document.activeElement can throw unexpectedly
// https://bugs.jquery.com/ticket/13393
    function safeActiveElement() {
        try {
            return document.activeElement;
        } catch ( err ) { }
    }

    function on( elem, types, selector, data, fn, one ) {
        var origFn, type;

        // Types can be a map of types/handlers
        if ( typeof types === "object" ) {

            // ( types-Object, selector, data )
            if ( typeof selector !== "string" ) {

                // ( types-Object, data )
                data = data || selector;
                selector = undefined;
            }
            for ( type in types ) {
                on( elem, type, selector, data, types[ type ], one );
            }
            return elem;
        }

        if ( data == null && fn == null ) {

            // ( types, fn )
            fn = selector;
            data = selector = undefined;
        } else if ( fn == null ) {
            if ( typeof selector === "string" ) {

                // ( types, selector, fn )
                fn = data;
                data = undefined;
            } else {

                // ( types, data, fn )
                fn = data;
                data = selector;
                selector = undefined;
            }
        }
        if ( fn === false ) {
            fn = returnFalse;
        } else if ( !fn ) {
            return elem;
        }

        if ( one === 1 ) {
            origFn = fn;
            fn = function( event ) {

                // Can use an empty set, since event contains the info
                jQuery().off( event );
                return origFn.apply( this, arguments );
            };

            // Use same guid so caller can remove using origFn
            fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
        }
        return elem.each( function() {
            jQuery.event.add( this, types, fn, data, selector );
        } );
    }

    /*
 * Helper functions for managing events -- not part of the public interface.
 * Props to Dean Edwards' addEvent library for many of the ideas.
 */
    jQuery.event = {

        global: {},

        add: function( elem, types, handler, data, selector ) {

            var handleObjIn, eventHandle, tmp,
                events, t, handleObj,
                special, handlers, type, namespaces, origType,
                elemData = dataPriv.get( elem );

            // Only attach events to objects that accept data
            if ( !acceptData( elem ) ) {
                return;
            }

            // Caller can pass in an object of custom data in lieu of the handler
            if ( handler.handler ) {
                handleObjIn = handler;
                handler = handleObjIn.handler;
                selector = handleObjIn.selector;
            }

            // Ensure that invalid selectors throw exceptions at attach time
            // Evaluate against documentElement in case elem is a non-element node (e.g., document)
            if ( selector ) {
                jQuery.find.matchesSelector( documentElement, selector );
            }

            // Make sure that the handler has a unique ID, used to find/remove it later
            if ( !handler.guid ) {
                handler.guid = jQuery.guid++;
            }

            // Init the element's event structure and main handler, if this is the first
            if ( !( events = elemData.events ) ) {
                events = elemData.events = Object.create( null );
            }
            if ( !( eventHandle = elemData.handle ) ) {
                eventHandle = elemData.handle = function( e ) {

                    // Discard the second event of a jQuery.event.trigger() and
                    // when an event is called after a page has unloaded
                    return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
                        jQuery.event.dispatch.apply( elem, arguments ) : undefined;
                };
            }

            // Handle multiple events separated by a space
            types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
            t = types.length;
            while ( t-- ) {
                tmp = rtypenamespace.exec( types[ t ] ) || [];
                type = origType = tmp[ 1 ];
                namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

                // There *must* be a type, no attaching namespace-only handlers
                if ( !type ) {
                    continue;
                }

                // If event changes its type, use the special event handlers for the changed type
                special = jQuery.event.special[ type ] || {};

                // If selector defined, determine special event api type, otherwise given type
                type = ( selector ? special.delegateType : special.bindType ) || type;

                // Update special based on newly reset type
                special = jQuery.event.special[ type ] || {};

                // handleObj is passed to all event handlers
                handleObj = jQuery.extend( {
                    type: type,
                    origType: origType,
                    data: data,
                    handler: handler,
                    guid: handler.guid,
                    selector: selector,
                    needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
                    namespace: namespaces.join( "." )
                }, handleObjIn );

                // Init the event handler queue if we're the first
                if ( !( handlers = events[ type ] ) ) {
                    handlers = events[ type ] = [];
                    handlers.delegateCount = 0;

                    // Only use addEventListener if the special events handler returns false
                    if ( !special.setup ||
                        special.setup.call( elem, data, namespaces, eventHandle ) === false ) {

                        if ( elem.addEventListener ) {
                            elem.addEventListener( type, eventHandle );
                        }
                    }
                }

                if ( special.add ) {
                    special.add.call( elem, handleObj );

                    if ( !handleObj.handler.guid ) {
                        handleObj.handler.guid = handler.guid;
                    }
                }

                // Add to the element's handler list, delegates in front
                if ( selector ) {
                    handlers.splice( handlers.delegateCount++, 0, handleObj );
                } else {
                    handlers.push( handleObj );
                }

                // Keep track of which events have ever been used, for event optimization
                jQuery.event.global[ type ] = true;
            }

        },

        // Detach an event or set of events from an element
        remove: function( elem, types, handler, selector, mappedTypes ) {

            var j, origCount, tmp,
                events, t, handleObj,
                special, handlers, type, namespaces, origType,
                elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );

            if ( !elemData || !( events = elemData.events ) ) {
                return;
            }

            // Once for each type.namespace in types; type may be omitted
            types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
            t = types.length;
            while ( t-- ) {
                tmp = rtypenamespace.exec( types[ t ] ) || [];
                type = origType = tmp[ 1 ];
                namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

                // Unbind all events (on this namespace, if provided) for the element
                if ( !type ) {
                    for ( type in events ) {
                        jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
                    }
                    continue;
                }

                special = jQuery.event.special[ type ] || {};
                type = ( selector ? special.delegateType : special.bindType ) || type;
                handlers = events[ type ] || [];
                tmp = tmp[ 2 ] &&
                    new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );

                // Remove matching events
                origCount = j = handlers.length;
                while ( j-- ) {
                    handleObj = handlers[ j ];

                    if ( ( mappedTypes || origType === handleObj.origType ) &&
                        ( !handler || handler.guid === handleObj.guid ) &&
                        ( !tmp || tmp.test( handleObj.namespace ) ) &&
                        ( !selector || selector === handleObj.selector ||
                            selector === "**" && handleObj.selector ) ) {
                        handlers.splice( j, 1 );

                        if ( handleObj.selector ) {
                            handlers.delegateCount--;
                        }
                        if ( special.remove ) {
                            special.remove.call( elem, handleObj );
                        }
                    }
                }

                // Remove generic event handler if we removed something and no more handlers exist
                // (avoids potential for endless recursion during removal of special event handlers)
                if ( origCount && !handlers.length ) {
                    if ( !special.teardown ||
                        special.teardown.call( elem, namespaces, elemData.handle ) === false ) {

                        jQuery.removeEvent( elem, type, elemData.handle );
                    }

                    delete events[ type ];
                }
            }

            // Remove data and the expando if it's no longer used
            if ( jQuery.isEmptyObject( events ) ) {
                dataPriv.remove( elem, "handle events" );
            }
        },

        dispatch: function( nativeEvent ) {

            var i, j, ret, matched, handleObj, handlerQueue,
                args = new Array( arguments.length ),

                // Make a writable jQuery.Event from the native event object
                event = jQuery.event.fix( nativeEvent ),

                handlers = (
                    dataPriv.get( this, "events" ) || Object.create( null )
                )[ event.type ] || [],
                special = jQuery.event.special[ event.type ] || {};

            // Use the fix-ed jQuery.Event rather than the (read-only) native event
            args[ 0 ] = event;

            for ( i = 1; i < arguments.length; i++ ) {
                args[ i ] = arguments[ i ];
            }

            event.delegateTarget = this;

            // Call the preDispatch hook for the mapped type, and let it bail if desired
            if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
                return;
            }

            // Determine handlers
            handlerQueue = jQuery.event.handlers.call( this, event, handlers );

            // Run delegates first; they may want to stop propagation beneath us
            i = 0;
            while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
                event.currentTarget = matched.elem;

                j = 0;
                while ( ( handleObj = matched.handlers[ j++ ] ) &&
                !event.isImmediatePropagationStopped() ) {

                    // If the event is namespaced, then each handler is only invoked if it is
                    // specially universal or its namespaces are a superset of the event's.
                    if ( !event.rnamespace || handleObj.namespace === false ||
                        event.rnamespace.test( handleObj.namespace ) ) {

                        event.handleObj = handleObj;
                        event.data = handleObj.data;

                        ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
                            handleObj.handler ).apply( matched.elem, args );

                        if ( ret !== undefined ) {
                            if ( ( event.result = ret ) === false ) {
                                event.preventDefault();
                                event.stopPropagation();
                            }
                        }
                    }
                }
            }

            // Call the postDispatch hook for the mapped type
            if ( special.postDispatch ) {
                special.postDispatch.call( this, event );
            }

            return event.result;
        },

        handlers: function( event, handlers ) {
            var i, handleObj, sel, matchedHandlers, matchedSelectors,
                handlerQueue = [],
                delegateCount = handlers.delegateCount,
                cur = event.target;

            // Find delegate handlers
            if ( delegateCount &&

                // Support: IE <=9
                // Black-hole SVG <use> instance trees (trac-13180)
                cur.nodeType &&

                // Support: Firefox <=42
                // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
                // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
                // Support: IE 11 only
                // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
                !( event.type === "click" && event.button >= 1 ) ) {

                for ( ; cur !== this; cur = cur.parentNode || this ) {

                    // Don't check non-elements (#13208)
                    // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
                    if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
                        matchedHandlers = [];
                        matchedSelectors = {};
                        for ( i = 0; i < delegateCount; i++ ) {
                            handleObj = handlers[ i ];

                            // Don't conflict with Object.prototype properties (#13203)
                            sel = handleObj.selector + " ";

                            if ( matchedSelectors[ sel ] === undefined ) {
                                matchedSelectors[ sel ] = handleObj.needsContext ?
                                    jQuery( sel, this ).index( cur ) > -1 :
                                    jQuery.find( sel, this, null, [ cur ] ).length;
                            }
                            if ( matchedSelectors[ sel ] ) {
                                matchedHandlers.push( handleObj );
                            }
                        }
                        if ( matchedHandlers.length ) {
                            handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
                        }
                    }
                }
            }

            // Add the remaining (directly-bound) handlers
            cur = this;
            if ( delegateCount < handlers.length ) {
                handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
            }

            return handlerQueue;
        },

        addProp: function( name, hook ) {
            Object.defineProperty( jQuery.Event.prototype, name, {
                enumerable: true,
                configurable: true,

                get: isFunction( hook ) ?
                    function() {
                        if ( this.originalEvent ) {
                            return hook( this.originalEvent );
                        }
                    } :
                    function() {
                        if ( this.originalEvent ) {
                            return this.originalEvent[ name ];
                        }
                    },

                set: function( value ) {
                    Object.defineProperty( this, name, {
                        enumerable: true,
                        configurable: true,
                        writable: true,
                        value: value
                    } );
                }
            } );
        },

        fix: function( originalEvent ) {
            return originalEvent[ jQuery.expando ] ?
                originalEvent :
                new jQuery.Event( originalEvent );
        },

        special: {
            load: {

                // Prevent triggered image.load events from bubbling to window.load
                noBubble: true
            },
            click: {

                // Utilize native event to ensure correct state for checkable inputs
                setup: function( data ) {

                    // For mutual compressibility with _default, replace `this` access with a local var.
                    // `|| data` is dead code meant only to preserve the variable through minification.
                    var el = this || data;

                    // Claim the first handler
                    if ( rcheckableType.test( el.type ) &&
                        el.click && nodeName( el, "input" ) ) {

                        // dataPriv.set( el, "click", ... )
                        leverageNative( el, "click", returnTrue );
                    }

                    // Return false to allow normal processing in the caller
                    return false;
                },
                trigger: function( data ) {

                    // For mutual compressibility with _default, replace `this` access with a local var.
                    // `|| data` is dead code meant only to preserve the variable through minification.
                    var el = this || data;

                    // Force setup before triggering a click
                    if ( rcheckableType.test( el.type ) &&
                        el.click && nodeName( el, "input" ) ) {

                        leverageNative( el, "click" );
                    }

                    // Return non-false to allow normal event-path propagation
                    return true;
                },

                // For cross-browser consistency, suppress native .click() on links
                // Also prevent it if we're currently inside a leveraged native-event stack
                _default: function( event ) {
                    var target = event.target;
                    return rcheckableType.test( target.type ) &&
                        target.click && nodeName( target, "input" ) &&
                        dataPriv.get( target, "click" ) ||
                        nodeName( target, "a" );
                }
            },

            beforeunload: {
                postDispatch: function( event ) {

                    // Support: Firefox 20+
                    // Firefox doesn't alert if the returnValue field is not set.
                    if ( event.result !== undefined && event.originalEvent ) {
                        event.originalEvent.returnValue = event.result;
                    }
                }
            }
        }
    };

// Ensure the presence of an event listener that handles manually-triggered
// synthetic events by interrupting progress until reinvoked in response to
// *native* events that it fires directly, ensuring that state changes have
// already occurred before other listeners are invoked.
    function leverageNative( el, type, expectSync ) {

        // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
        if ( !expectSync ) {
            if ( dataPriv.get( el, type ) === undefined ) {
                jQuery.event.add( el, type, returnTrue );
            }
            return;
        }

        // Register the controller as a special universal handler for all event namespaces
        dataPriv.set( el, type, false );
        jQuery.event.add( el, type, {
            namespace: false,
            handler: function( event ) {
                var notAsync, result,
                    saved = dataPriv.get( this, type );

                if ( ( event.isTrigger & 1 ) && this[ type ] ) {

                    // Interrupt processing of the outer synthetic .trigger()ed event
                    // Saved data should be false in such cases, but might be a leftover capture object
                    // from an async native handler (gh-4350)
                    if ( !saved.length ) {

                        // Store arguments for use when handling the inner native event
                        // There will always be at least one argument (an event object), so this array
                        // will not be confused with a leftover capture object.
                        saved = slice.call( arguments );
                        dataPriv.set( this, type, saved );

                        // Trigger the native event and capture its result
                        // Support: IE <=9 - 11+
                        // focus() and blur() are asynchronous
                        notAsync = expectSync( this, type );
                        this[ type ]();
                        result = dataPriv.get( this, type );
                        if ( saved !== result || notAsync ) {
                            dataPriv.set( this, type, false );
                        } else {
                            result = {};
                        }
                        if ( saved !== result ) {

                            // Cancel the outer synthetic event
                            event.stopImmediatePropagation();
                            event.preventDefault();

                            // Support: Chrome 86+
                            // In Chrome, if an element having a focusout handler is blurred by
                            // clicking outside of it, it invokes the handler synchronously. If
                            // that handler calls `.remove()` on the element, the data is cleared,
                            // leaving `result` undefined. We need to guard against this.
                            return result && result.value;
                        }

                        // If this is an inner synthetic event for an event with a bubbling surrogate
                        // (focus or blur), assume that the surrogate already propagated from triggering the
                        // native event and prevent that from happening again here.
                        // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
                        // bubbling surrogate propagates *after* the non-bubbling base), but that seems
                        // less bad than duplication.
                    } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
                        event.stopPropagation();
                    }

                    // If this is a native event triggered above, everything is now in order
                    // Fire an inner synthetic event with the original arguments
                } else if ( saved.length ) {

                    // ...and capture the result
                    dataPriv.set( this, type, {
                        value: jQuery.event.trigger(

                            // Support: IE <=9 - 11+
                            // Extend with the prototype to reset the above stopImmediatePropagation()
                            jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
                            saved.slice( 1 ),
                            this
                        )
                    } );

                    // Abort handling of the native event
                    event.stopImmediatePropagation();
                }
            }
        } );
    }

    jQuery.removeEvent = function( elem, type, handle ) {

        // This "if" is needed for plain objects
        if ( elem.removeEventListener ) {
            elem.removeEventListener( type, handle );
        }
    };

    jQuery.Event = function( src, props ) {

        // Allow instantiation without the 'new' keyword
        if ( !( this instanceof jQuery.Event ) ) {
            return new jQuery.Event( src, props );
        }

        // Event object
        if ( src && src.type ) {
            this.originalEvent = src;
            this.type = src.type;

            // Events bubbling up the document may have been marked as prevented
            // by a handler lower down the tree; reflect the correct value.
            this.isDefaultPrevented = src.defaultPrevented ||
            src.defaultPrevented === undefined &&

            // Support: Android <=2.3 only
            src.returnValue === false ?
                returnTrue :
                returnFalse;

            // Create target properties
            // Support: Safari <=6 - 7 only
            // Target should not be a text node (#504, #13143)
            this.target = ( src.target && src.target.nodeType === 3 ) ?
                src.target.parentNode :
                src.target;

            this.currentTarget = src.currentTarget;
            this.relatedTarget = src.relatedTarget;

            // Event type
        } else {
            this.type = src;
        }

        // Put explicitly provided properties onto the event object
        if ( props ) {
            jQuery.extend( this, props );
        }

        // Create a timestamp if incoming event doesn't have one
        this.timeStamp = src && src.timeStamp || Date.now();

        // Mark it as fixed
        this[ jQuery.expando ] = true;
    };

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
    jQuery.Event.prototype = {
        constructor: jQuery.Event,
        isDefaultPrevented: returnFalse,
        isPropagationStopped: returnFalse,
        isImmediatePropagationStopped: returnFalse,
        isSimulated: false,

        preventDefault: function() {
            var e = this.originalEvent;

            this.isDefaultPrevented = returnTrue;

            if ( e && !this.isSimulated ) {
                e.preventDefault();
            }
        },
        stopPropagation: function() {
            var e = this.originalEvent;

            this.isPropagationStopped = returnTrue;

            if ( e && !this.isSimulated ) {
                e.stopPropagation();
            }
        },
        stopImmediatePropagation: function() {
            var e = this.originalEvent;

            this.isImmediatePropagationStopped = returnTrue;

            if ( e && !this.isSimulated ) {
                e.stopImmediatePropagation();
            }

            this.stopPropagation();
        }
    };

// Includes all common event props including KeyEvent and MouseEvent specific props
    jQuery.each( {
        altKey: true,
        bubbles: true,
        cancelable: true,
        changedTouches: true,
        ctrlKey: true,
        detail: true,
        eventPhase: true,
        metaKey: true,
        pageX: true,
        pageY: true,
        shiftKey: true,
        view: true,
        "char": true,
        code: true,
        charCode: true,
        key: true,
        keyCode: true,
        button: true,
        buttons: true,
        clientX: true,
        clientY: true,
        offsetX: true,
        offsetY: true,
        pointerId: true,
        pointerType: true,
        screenX: true,
        screenY: true,
        targetTouches: true,
        toElement: true,
        touches: true,
        which: true
    }, jQuery.event.addProp );

    jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
        jQuery.event.special[ type ] = {

            // Utilize native event if possible so blur/focus sequence is correct
            setup: function() {

                // Claim the first handler
                // dataPriv.set( this, "focus", ... )
                // dataPriv.set( this, "blur", ... )
                leverageNative( this, type, expectSync );

                // Return false to allow normal processing in the caller
                return false;
            },
            trigger: function() {

                // Force setup before trigger
                leverageNative( this, type );

                // Return non-false to allow normal event-path propagation
                return true;
            },

            // Suppress native focus or blur as it's already being fired
            // in leverageNative.
            _default: function() {
                return true;
            },

            delegateType: delegateType
        };
    } );

// Create mouseenter/leave events using mouseover/out and event-time checks
// so that event delegation works in jQuery.
// Do the same for pointerenter/pointerleave and pointerover/pointerout
//
// Support: Safari 7 only
// Safari sends mouseenter too often; see:
// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
// for the description of the bug (it existed in older Chrome versions as well).
    jQuery.each( {
        mouseenter: "mouseover",
        mouseleave: "mouseout",
        pointerenter: "pointerover",
        pointerleave: "pointerout"
    }, function( orig, fix ) {
        jQuery.event.special[ orig ] = {
            delegateType: fix,
            bindType: fix,

            handle: function( event ) {
                var ret,
                    target = this,
                    related = event.relatedTarget,
                    handleObj = event.handleObj;

                // For mouseenter/leave call the handler if related is outside the target.
                // NB: No relatedTarget if the mouse left/entered the browser window
                if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
                    event.type = handleObj.origType;
                    ret = handleObj.handler.apply( this, arguments );
                    event.type = fix;
                }
                return ret;
            }
        };
    } );

    jQuery.fn.extend( {

        on: function( types, selector, data, fn ) {
            return on( this, types, selector, data, fn );
        },
        one: function( types, selector, data, fn ) {
            return on( this, types, selector, data, fn, 1 );
        },
        off: function( types, selector, fn ) {
            var handleObj, type;
            if ( types && types.preventDefault && types.handleObj ) {

                // ( event )  dispatched jQuery.Event
                handleObj = types.handleObj;
                jQuery( types.delegateTarget ).off(
                    handleObj.namespace ?
                        handleObj.origType + "." + handleObj.namespace :
                        handleObj.origType,
                    handleObj.selector,
                    handleObj.handler
                );
                return this;
            }
            if ( typeof types === "object" ) {

                // ( types-object [, selector] )
                for ( type in types ) {
                    this.off( type, selector, types[ type ] );
                }
                return this;
            }
            if ( selector === false || typeof selector === "function" ) {

                // ( types [, fn] )
                fn = selector;
                selector = undefined;
            }
            if ( fn === false ) {
                fn = returnFalse;
            }
            return this.each( function() {
                jQuery.event.remove( this, types, fn, selector );
            } );
        }
    } );


    var

        // Support: IE <=10 - 11, Edge 12 - 13 only
        // In IE/Edge using regex groups here causes severe slowdowns.
        // See https://connect.microsoft.com/IE/feedback/details/1736512/
        rnoInnerhtml = /<script|<style|<link/i,

        // checked="checked" or checked
        rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
        rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;

// Prefer a tbody over its parent table for containing new rows
    function manipulationTarget( elem, content ) {
        if ( nodeName( elem, "table" ) &&
            nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {

            return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
        }

        return elem;
    }

// Replace/restore the type attribute of script elements for safe DOM manipulation
    function disableScript( elem ) {
        elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
        return elem;
    }
    function restoreScript( elem ) {
        if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
            elem.type = elem.type.slice( 5 );
        } else {
            elem.removeAttribute( "type" );
        }

        return elem;
    }

    function cloneCopyEvent( src, dest ) {
        var i, l, type, pdataOld, udataOld, udataCur, events;

        if ( dest.nodeType !== 1 ) {
            return;
        }

        // 1. Copy private data: events, handlers, etc.
        if ( dataPriv.hasData( src ) ) {
            pdataOld = dataPriv.get( src );
            events = pdataOld.events;

            if ( events ) {
                dataPriv.remove( dest, "handle events" );

                for ( type in events ) {
                    for ( i = 0, l = events[ type ].length; i < l; i++ ) {
                        jQuery.event.add( dest, type, events[ type ][ i ] );
                    }
                }
            }
        }

        // 2. Copy user data
        if ( dataUser.hasData( src ) ) {
            udataOld = dataUser.access( src );
            udataCur = jQuery.extend( {}, udataOld );

            dataUser.set( dest, udataCur );
        }
    }

// Fix IE bugs, see support tests
    function fixInput( src, dest ) {
        var nodeName = dest.nodeName.toLowerCase();

        // Fails to persist the checked state of a cloned checkbox or radio button.
        if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
            dest.checked = src.checked;

            // Fails to return the selected option to the default selected state when cloning options
        } else if ( nodeName === "input" || nodeName === "textarea" ) {
            dest.defaultValue = src.defaultValue;
        }
    }

    function domManip( collection, args, callback, ignored ) {

        // Flatten any nested arrays
        args = flat( args );

        var fragment, first, scripts, hasScripts, node, doc,
            i = 0,
            l = collection.length,
            iNoClone = l - 1,
            value = args[ 0 ],
            valueIsFunction = isFunction( value );

        // We can't cloneNode fragments that contain checked, in WebKit
        if ( valueIsFunction ||
            ( l > 1 && typeof value === "string" &&
                !support.checkClone && rchecked.test( value ) ) ) {
            return collection.each( function( index ) {
                var self = collection.eq( index );
                if ( valueIsFunction ) {
                    args[ 0 ] = value.call( this, index, self.html() );
                }
                domManip( self, args, callback, ignored );
            } );
        }

        if ( l ) {
            fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
            first = fragment.firstChild;

            if ( fragment.childNodes.length === 1 ) {
                fragment = first;
            }

            // Require either new content or an interest in ignored elements to invoke the callback
            if ( first || ignored ) {
                scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
                hasScripts = scripts.length;

                // Use the original fragment for the last item
                // instead of the first because it can end up
                // being emptied incorrectly in certain situations (#8070).
                for ( ; i < l; i++ ) {
                    node = fragment;

                    if ( i !== iNoClone ) {
                        node = jQuery.clone( node, true, true );

                        // Keep references to cloned scripts for later restoration
                        if ( hasScripts ) {

                            // Support: Android <=4.0 only, PhantomJS 1 only
                            // push.apply(_, arraylike) throws on ancient WebKit
                            jQuery.merge( scripts, getAll( node, "script" ) );
                        }
                    }

                    callback.call( collection[ i ], node, i );
                }

                if ( hasScripts ) {
                    doc = scripts[ scripts.length - 1 ].ownerDocument;

                    // Reenable scripts
                    jQuery.map( scripts, restoreScript );

                    // Evaluate executable scripts on first document insertion
                    for ( i = 0; i < hasScripts; i++ ) {
                        node = scripts[ i ];
                        if ( rscriptType.test( node.type || "" ) &&
                            !dataPriv.access( node, "globalEval" ) &&
                            jQuery.contains( doc, node ) ) {

                            if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {

                                // Optional AJAX dependency, but won't run scripts if not present
                                if ( jQuery._evalUrl && !node.noModule ) {
                                    jQuery._evalUrl( node.src, {
                                        nonce: node.nonce || node.getAttribute( "nonce" )
                                    }, doc );
                                }
                            } else {
                                DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
                            }
                        }
                    }
                }
            }
        }

        return collection;
    }

    function remove( elem, selector, keepData ) {
        var node,
            nodes = selector ? jQuery.filter( selector, elem ) : elem,
            i = 0;

        for ( ; ( node = nodes[ i ] ) != null; i++ ) {
            if ( !keepData && node.nodeType === 1 ) {
                jQuery.cleanData( getAll( node ) );
            }

            if ( node.parentNode ) {
                if ( keepData && isAttached( node ) ) {
                    setGlobalEval( getAll( node, "script" ) );
                }
                node.parentNode.removeChild( node );
            }
        }

        return elem;
    }

    jQuery.extend( {
        htmlPrefilter: function( html ) {
            return html;
        },

        clone: function( elem, dataAndEvents, deepDataAndEvents ) {
            var i, l, srcElements, destElements,
                clone = elem.cloneNode( true ),
                inPage = isAttached( elem );

            // Fix IE cloning issues
            if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
                !jQuery.isXMLDoc( elem ) ) {

                // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
                destElements = getAll( clone );
                srcElements = getAll( elem );

                for ( i = 0, l = srcElements.length; i < l; i++ ) {
                    fixInput( srcElements[ i ], destElements[ i ] );
                }
            }

            // Copy the events from the original to the clone
            if ( dataAndEvents ) {
                if ( deepDataAndEvents ) {
                    srcElements = srcElements || getAll( elem );
                    destElements = destElements || getAll( clone );

                    for ( i = 0, l = srcElements.length; i < l; i++ ) {
                        cloneCopyEvent( srcElements[ i ], destElements[ i ] );
                    }
                } else {
                    cloneCopyEvent( elem, clone );
                }
            }

            // Preserve script evaluation history
            destElements = getAll( clone, "script" );
            if ( destElements.length > 0 ) {
                setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
            }

            // Return the cloned set
            return clone;
        },

        cleanData: function( elems ) {
            var data, elem, type,
                special = jQuery.event.special,
                i = 0;

            for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
                if ( acceptData( elem ) ) {
                    if ( ( data = elem[ dataPriv.expando ] ) ) {
                        if ( data.events ) {
                            for ( type in data.events ) {
                                if ( special[ type ] ) {
                                    jQuery.event.remove( elem, type );

                                    // This is a shortcut to avoid jQuery.event.remove's overhead
                                } else {
                                    jQuery.removeEvent( elem, type, data.handle );
                                }
                            }
                        }

                        // Support: Chrome <=35 - 45+
                        // Assign undefined instead of using delete, see Data#remove
                        elem[ dataPriv.expando ] = undefined;
                    }
                    if ( elem[ dataUser.expando ] ) {

                        // Support: Chrome <=35 - 45+
                        // Assign undefined instead of using delete, see Data#remove
                        elem[ dataUser.expando ] = undefined;
                    }
                }
            }
        }
    } );

    jQuery.fn.extend( {
        detach: function( selector ) {
            return remove( this, selector, true );
        },

        remove: function( selector ) {
            return remove( this, selector );
        },

        text: function( value ) {
            return access( this, function( value ) {
                return value === undefined ?
                    jQuery.text( this ) :
                    this.empty().each( function() {
                        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
                            this.textContent = value;
                        }
                    } );
            }, null, value, arguments.length );
        },

        append: function() {
            return domManip( this, arguments, function( elem ) {
                if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
                    var target = manipulationTarget( this, elem );
                    target.appendChild( elem );
                }
            } );
        },

        prepend: function() {
            return domManip( this, arguments, function( elem ) {
                if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
                    var target = manipulationTarget( this, elem );
                    target.insertBefore( elem, target.firstChild );
                }
            } );
        },

        before: function() {
            return domManip( this, arguments, function( elem ) {
                if ( this.parentNode ) {
                    this.parentNode.insertBefore( elem, this );
                }
            } );
        },

        after: function() {
            return domManip( this, arguments, function( elem ) {
                if ( this.parentNode ) {
                    this.parentNode.insertBefore( elem, this.nextSibling );
                }
            } );
        },

        empty: function() {
            var elem,
                i = 0;

            for ( ; ( elem = this[ i ] ) != null; i++ ) {
                if ( elem.nodeType === 1 ) {

                    // Prevent memory leaks
                    jQuery.cleanData( getAll( elem, false ) );

                    // Remove any remaining nodes
                    elem.textContent = "";
                }
            }

            return this;
        },

        clone: function( dataAndEvents, deepDataAndEvents ) {
            dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
            deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

            return this.map( function() {
                return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
            } );
        },

        html: function( value ) {
            return access( this, function( value ) {
                var elem = this[ 0 ] || {},
                    i = 0,
                    l = this.length;

                if ( value === undefined && elem.nodeType === 1 ) {
                    return elem.innerHTML;
                }

                // See if we can take a shortcut and just use innerHTML
                if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
                    !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {

                    value = jQuery.htmlPrefilter( value );

                    try {
                        for ( ; i < l; i++ ) {
                            elem = this[ i ] || {};

                            // Remove element nodes and prevent memory leaks
                            if ( elem.nodeType === 1 ) {
                                jQuery.cleanData( getAll( elem, false ) );
                                elem.innerHTML = value;
                            }
                        }

                        elem = 0;

                        // If using innerHTML throws an exception, use the fallback method
                    } catch ( e ) {}
                }

                if ( elem ) {
                    this.empty().append( value );
                }
            }, null, value, arguments.length );
        },

        replaceWith: function() {
            var ignored = [];

            // Make the changes, replacing each non-ignored context element with the new content
            return domManip( this, arguments, function( elem ) {
                var parent = this.parentNode;

                if ( jQuery.inArray( this, ignored ) < 0 ) {
                    jQuery.cleanData( getAll( this ) );
                    if ( parent ) {
                        parent.replaceChild( elem, this );
                    }
                }

                // Force callback invocation
            }, ignored );
        }
    } );

    jQuery.each( {
        appendTo: "append",
        prependTo: "prepend",
        insertBefore: "before",
        insertAfter: "after",
        replaceAll: "replaceWith"
    }, function( name, original ) {
        jQuery.fn[ name ] = function( selector ) {
            var elems,
                ret = [],
                insert = jQuery( selector ),
                last = insert.length - 1,
                i = 0;

            for ( ; i <= last; i++ ) {
                elems = i === last ? this : this.clone( true );
                jQuery( insert[ i ] )[ original ]( elems );

                // Support: Android <=4.0 only, PhantomJS 1 only
                // .get() because push.apply(_, arraylike) throws on ancient WebKit
                push.apply( ret, elems.get() );
            }

            return this.pushStack( ret );
        };
    } );
    var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );

    var getStyles = function( elem ) {

        // Support: IE <=11 only, Firefox <=30 (#15098, #14150)
        // IE throws on elements created in popups
        // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
        var view = elem.ownerDocument.defaultView;

        if ( !view || !view.opener ) {
            view = window;
        }

        return view.getComputedStyle( elem );
    };

    var swap = function( elem, options, callback ) {
        var ret, name,
            old = {};

        // Remember the old values, and insert the new ones
        for ( name in options ) {
            old[ name ] = elem.style[ name ];
            elem.style[ name ] = options[ name ];
        }

        ret = callback.call( elem );

        // Revert the old values
        for ( name in options ) {
            elem.style[ name ] = old[ name ];
        }

        return ret;
    };


    var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );



    ( function() {

        // Executing both pixelPosition & boxSizingReliable tests require only one layout
        // so they're executed at the same time to save the second computation.
        function computeStyleTests() {

            // This is a singleton, we need to execute it only once
            if ( !div ) {
                return;
            }

            container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
                "margin-top:1px;padding:0;border:0";
            div.style.cssText =
                "position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
                "margin:auto;border:1px;padding:1px;" +
                "width:60%;top:1%";
            documentElement.appendChild( container ).appendChild( div );

            var divStyle = window.getComputedStyle( div );
            pixelPositionVal = divStyle.top !== "1%";

            // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
            reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;

            // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
            // Some styles come back with percentage values, even though they shouldn't
            div.style.right = "60%";
            pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;

            // Support: IE 9 - 11 only
            // Detect misreporting of content dimensions for box-sizing:border-box elements
            boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;

            // Support: IE 9 only
            // Detect overflow:scroll screwiness (gh-3699)
            // Support: Chrome <=64
            // Don't get tricked when zoom affects offsetWidth (gh-4029)
            div.style.position = "absolute";
            scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;

            documentElement.removeChild( container );

            // Nullify the div so it wouldn't be stored in the memory and
            // it will also be a sign that checks already performed
            div = null;
        }

        function roundPixelMeasures( measure ) {
            return Math.round( parseFloat( measure ) );
        }

        var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
            reliableTrDimensionsVal, reliableMarginLeftVal,
            container = document.createElement( "div" ),
            div = document.createElement( "div" );

        // Finish early in limited (non-browser) environments
        if ( !div.style ) {
            return;
        }

        // Support: IE <=9 - 11 only
        // Style of cloned element affects source element cloned (#8908)
        div.style.backgroundClip = "content-box";
        div.cloneNode( true ).style.backgroundClip = "";
        support.clearCloneStyle = div.style.backgroundClip === "content-box";

        jQuery.extend( support, {
            boxSizingReliable: function() {
                computeStyleTests();
                return boxSizingReliableVal;
            },
            pixelBoxStyles: function() {
                computeStyleTests();
                return pixelBoxStylesVal;
            },
            pixelPosition: function() {
                computeStyleTests();
                return pixelPositionVal;
            },
            reliableMarginLeft: function() {
                computeStyleTests();
                return reliableMarginLeftVal;
            },
            scrollboxSize: function() {
                computeStyleTests();
                return scrollboxSizeVal;
            },

            // Support: IE 9 - 11+, Edge 15 - 18+
            // IE/Edge misreport `getComputedStyle` of table rows with width/height
            // set in CSS while `offset*` properties report correct values.
            // Behavior in IE 9 is more subtle than in newer versions & it passes
            // some versions of this test; make sure not to make it pass there!
            //
            // Support: Firefox 70+
            // Only Firefox includes border widths
            // in computed dimensions. (gh-4529)
            reliableTrDimensions: function() {
                var table, tr, trChild, trStyle;
                if ( reliableTrDimensionsVal == null ) {
                    table = document.createElement( "table" );
                    tr = document.createElement( "tr" );
                    trChild = document.createElement( "div" );

                    table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
                    tr.style.cssText = "border:1px solid";

                    // Support: Chrome 86+
                    // Height set through cssText does not get applied.
                    // Computed height then comes back as 0.
                    tr.style.height = "1px";
                    trChild.style.height = "9px";

                    // Support: Android 8 Chrome 86+
                    // In our bodyBackground.html iframe,
                    // display for all div elements is set to "inline",
                    // which causes a problem only in Android 8 Chrome 86.
                    // Ensuring the div is display: block
                    // gets around this issue.
                    trChild.style.display = "block";

                    documentElement
                        .appendChild( table )
                        .appendChild( tr )
                        .appendChild( trChild );

                    trStyle = window.getComputedStyle( tr );
                    reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +
                        parseInt( trStyle.borderTopWidth, 10 ) +
                        parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;

                    documentElement.removeChild( table );
                }
                return reliableTrDimensionsVal;
            }
        } );
    } )();


    function curCSS( elem, name, computed ) {
        var width, minWidth, maxWidth, ret,

            // Support: Firefox 51+
            // Retrieving style before computed somehow
            // fixes an issue with getting wrong values
            // on detached elements
            style = elem.style;

        computed = computed || getStyles( elem );

        // getPropertyValue is needed for:
        //   .css('filter') (IE 9 only, #12537)
        //   .css('--customProperty) (#3144)
        if ( computed ) {
            ret = computed.getPropertyValue( name ) || computed[ name ];

            if ( ret === "" && !isAttached( elem ) ) {
                ret = jQuery.style( elem, name );
            }

            // A tribute to the "awesome hack by Dean Edwards"
            // Android Browser returns percentage for some values,
            // but width seems to be reliably pixels.
            // This is against the CSSOM draft spec:
            // https://drafts.csswg.org/cssom/#resolved-values
            if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {

                // Remember the original values
                width = style.width;
                minWidth = style.minWidth;
                maxWidth = style.maxWidth;

                // Put in the new values to get a computed value out
                style.minWidth = style.maxWidth = style.width = ret;
                ret = computed.width;

                // Revert the changed values
                style.width = width;
                style.minWidth = minWidth;
                style.maxWidth = maxWidth;
            }
        }

        return ret !== undefined ?

            // Support: IE <=9 - 11 only
            // IE returns zIndex value as an integer.
            ret + "" :
            ret;
    }


    function addGetHookIf( conditionFn, hookFn ) {

        // Define the hook, we'll check on the first run if it's really needed.
        return {
            get: function() {
                if ( conditionFn() ) {

                    // Hook not needed (or it's not possible to use it due
                    // to missing dependency), remove it.
                    delete this.get;
                    return;
                }

                // Hook needed; redefine it so that the support test is not executed again.
                return ( this.get = hookFn ).apply( this, arguments );
            }
        };
    }


    var cssPrefixes = [ "Webkit", "Moz", "ms" ],
        emptyStyle = document.createElement( "div" ).style,
        vendorProps = {};

// Return a vendor-prefixed property or undefined
    function vendorPropName( name ) {

        // Check for vendor prefixed names
        var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
            i = cssPrefixes.length;

        while ( i-- ) {
            name = cssPrefixes[ i ] + capName;
            if ( name in emptyStyle ) {
                return name;
            }
        }
    }

// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
    function finalPropName( name ) {
        var final = jQuery.cssProps[ name ] || vendorProps[ name ];

        if ( final ) {
            return final;
        }
        if ( name in emptyStyle ) {
            return name;
        }
        return vendorProps[ name ] = vendorPropName( name ) || name;
    }


    var

        // Swappable if display is none or starts with table
        // except "table", "table-cell", or "table-caption"
        // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
        rdisplayswap = /^(none|table(?!-c[ea]).+)/,
        rcustomProp = /^--/,
        cssShow = { position: "absolute", visibility: "hidden", display: "block" },
        cssNormalTransform = {
            letterSpacing: "0",
            fontWeight: "400"
        };

    function setPositiveNumber( _elem, value, subtract ) {

        // Any relative (+/-) values have already been
        // normalized at this point
        var matches = rcssNum.exec( value );
        return matches ?

            // Guard against undefined "subtract", e.g., when used as in cssHooks
            Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
            value;
    }

    function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
        var i = dimension === "width" ? 1 : 0,
            extra = 0,
            delta = 0;

        // Adjustment may not be necessary
        if ( box === ( isBorderBox ? "border" : "content" ) ) {
            return 0;
        }

        for ( ; i < 4; i += 2 ) {

            // Both box models exclude margin
            if ( box === "margin" ) {
                delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
            }

            // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
            if ( !isBorderBox ) {

                // Add padding
                delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );

                // For "border" or "margin", add border
                if ( box !== "padding" ) {
                    delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );

                    // But still keep track of it otherwise
                } else {
                    extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                }

                // If we get here with a border-box (content + padding + border), we're seeking "content" or
                // "padding" or "margin"
            } else {

                // For "content", subtract padding
                if ( box === "content" ) {
                    delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
                }

                // For "content" or "padding", subtract border
                if ( box !== "margin" ) {
                    delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                }
            }
        }

        // Account for positive content-box scroll gutter when requested by providing computedVal
        if ( !isBorderBox && computedVal >= 0 ) {

            // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
            // Assuming integer scroll gutter, subtract the rest and round down
            delta += Math.max( 0, Math.ceil(
                elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
                computedVal -
                delta -
                extra -
                0.5

                // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
                // Use an explicit zero to avoid NaN (gh-3964)
            ) ) || 0;
        }

        return delta;
    }

    function getWidthOrHeight( elem, dimension, extra ) {

        // Start with computed style
        var styles = getStyles( elem ),

            // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
            // Fake content-box until we know it's needed to know the true value.
            boxSizingNeeded = !support.boxSizingReliable() || extra,
            isBorderBox = boxSizingNeeded &&
                jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
            valueIsBorderBox = isBorderBox,

            val = curCSS( elem, dimension, styles ),
            offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );

        // Support: Firefox <=54
        // Return a confounding non-pixel value or feign ignorance, as appropriate.
        if ( rnumnonpx.test( val ) ) {
            if ( !extra ) {
                return val;
            }
            val = "auto";
        }


        // Support: IE 9 - 11 only
        // Use offsetWidth/offsetHeight for when box sizing is unreliable.
        // In those cases, the computed value can be trusted to be border-box.
        if ( ( !support.boxSizingReliable() && isBorderBox ||

                // Support: IE 10 - 11+, Edge 15 - 18+
                // IE/Edge misreport `getComputedStyle` of table rows with width/height
                // set in CSS while `offset*` properties report correct values.
                // Interestingly, in some cases IE 9 doesn't suffer from this issue.
                !support.reliableTrDimensions() && nodeName( elem, "tr" ) ||

                // Fall back to offsetWidth/offsetHeight when value is "auto"
                // This happens for inline elements with no explicit setting (gh-3571)
                val === "auto" ||

                // Support: Android <=4.1 - 4.3 only
                // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
                !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&

            // Make sure the element is visible & connected
            elem.getClientRects().length ) {

            isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";

            // Where available, offsetWidth/offsetHeight approximate border box dimensions.
            // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
            // retrieved value as a content box dimension.
            valueIsBorderBox = offsetProp in elem;
            if ( valueIsBorderBox ) {
                val = elem[ offsetProp ];
            }
        }

        // Normalize "" and auto
        val = parseFloat( val ) || 0;

        // Adjust for the element's box model
        return ( val +
            boxModelAdjustment(
                elem,
                dimension,
                extra || ( isBorderBox ? "border" : "content" ),
                valueIsBorderBox,
                styles,

                // Provide the current computed size to request scroll gutter calculation (gh-3589)
                val
            )
        ) + "px";
    }

    jQuery.extend( {

        // Add in style property hooks for overriding the default
        // behavior of getting and setting a style property
        cssHooks: {
            opacity: {
                get: function( elem, computed ) {
                    if ( computed ) {

                        // We should always get a number back from opacity
                        var ret = curCSS( elem, "opacity" );
                        return ret === "" ? "1" : ret;
                    }
                }
            }
        },

        // Don't automatically add "px" to these possibly-unitless properties
        cssNumber: {
            "animationIterationCount": true,
            "columnCount": true,
            "fillOpacity": true,
            "flexGrow": true,
            "flexShrink": true,
            "fontWeight": true,
            "gridArea": true,
            "gridColumn": true,
            "gridColumnEnd": true,
            "gridColumnStart": true,
            "gridRow": true,
            "gridRowEnd": true,
            "gridRowStart": true,
            "lineHeight": true,
            "opacity": true,
            "order": true,
            "orphans": true,
            "widows": true,
            "zIndex": true,
            "zoom": true
        },

        // Add in properties whose names you wish to fix before
        // setting or getting the value
        cssProps: {},

        // Get and set the style property on a DOM Node
        style: function( elem, name, value, extra ) {

            // Don't set styles on text and comment nodes
            if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
                return;
            }

            // Make sure that we're working with the right name
            var ret, type, hooks,
                origName = camelCase( name ),
                isCustomProp = rcustomProp.test( name ),
                style = elem.style;

            // Make sure that we're working with the right name. We don't
            // want to query the value if it is a CSS custom property
            // since they are user-defined.
            if ( !isCustomProp ) {
                name = finalPropName( origName );
            }

            // Gets hook for the prefixed version, then unprefixed version
            hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

            // Check if we're setting a value
            if ( value !== undefined ) {
                type = typeof value;

                // Convert "+=" or "-=" to relative numbers (#7345)
                if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
                    value = adjustCSS( elem, name, ret );

                    // Fixes bug #9237
                    type = "number";
                }

                // Make sure that null and NaN values aren't set (#7116)
                if ( value == null || value !== value ) {
                    return;
                }

                // If a number was passed in, add the unit (except for certain CSS properties)
                // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
                // "px" to a few hardcoded values.
                if ( type === "number" && !isCustomProp ) {
                    value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
                }

                // background-* props affect original clone's values
                if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
                    style[ name ] = "inherit";
                }

                // If a hook was provided, use that value, otherwise just set the specified value
                if ( !hooks || !( "set" in hooks ) ||
                    ( value = hooks.set( elem, value, extra ) ) !== undefined ) {

                    if ( isCustomProp ) {
                        style.setProperty( name, value );
                    } else {
                        style[ name ] = value;
                    }
                }

            } else {

                // If a hook was provided get the non-computed value from there
                if ( hooks && "get" in hooks &&
                    ( ret = hooks.get( elem, false, extra ) ) !== undefined ) {

                    return ret;
                }

                // Otherwise just get the value from the style object
                return style[ name ];
            }
        },

        css: function( elem, name, extra, styles ) {
            var val, num, hooks,
                origName = camelCase( name ),
                isCustomProp = rcustomProp.test( name );

            // Make sure that we're working with the right name. We don't
            // want to modify the value if it is a CSS custom property
            // since they are user-defined.
            if ( !isCustomProp ) {
                name = finalPropName( origName );
            }

            // Try prefixed name followed by the unprefixed name
            hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

            // If a hook was provided get the computed value from there
            if ( hooks && "get" in hooks ) {
                val = hooks.get( elem, true, extra );
            }

            // Otherwise, if a way to get the computed value exists, use that
            if ( val === undefined ) {
                val = curCSS( elem, name, styles );
            }

            // Convert "normal" to computed value
            if ( val === "normal" && name in cssNormalTransform ) {
                val = cssNormalTransform[ name ];
            }

            // Make numeric if forced or a qualifier was provided and val looks numeric
            if ( extra === "" || extra ) {
                num = parseFloat( val );
                return extra === true || isFinite( num ) ? num || 0 : val;
            }

            return val;
        }
    } );

    jQuery.each( [ "height", "width" ], function( _i, dimension ) {
        jQuery.cssHooks[ dimension ] = {
            get: function( elem, computed, extra ) {
                if ( computed ) {

                    // Certain elements can have dimension info if we invisibly show them
                    // but it must have a current display style that would benefit
                    return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&

                    // Support: Safari 8+
                    // Table columns in Safari have non-zero offsetWidth & zero
                    // getBoundingClientRect().width unless display is changed.
                    // Support: IE <=11 only
                    // Running getBoundingClientRect on a disconnected node
                    // in IE throws an error.
                    ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
                        swap( elem, cssShow, function() {
                            return getWidthOrHeight( elem, dimension, extra );
                        } ) :
                        getWidthOrHeight( elem, dimension, extra );
                }
            },

            set: function( elem, value, extra ) {
                var matches,
                    styles = getStyles( elem ),

                    // Only read styles.position if the test has a chance to fail
                    // to avoid forcing a reflow.
                    scrollboxSizeBuggy = !support.scrollboxSize() &&
                        styles.position === "absolute",

                    // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
                    boxSizingNeeded = scrollboxSizeBuggy || extra,
                    isBorderBox = boxSizingNeeded &&
                        jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
                    subtract = extra ?
                        boxModelAdjustment(
                            elem,
                            dimension,
                            extra,
                            isBorderBox,
                            styles
                        ) :
                        0;

                // Account for unreliable border-box dimensions by comparing offset* to computed and
                // faking a content-box to get border and padding (gh-3699)
                if ( isBorderBox && scrollboxSizeBuggy ) {
                    subtract -= Math.ceil(
                        elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
                        parseFloat( styles[ dimension ] ) -
                        boxModelAdjustment( elem, dimension, "border", false, styles ) -
                        0.5
                    );
                }

                // Convert to pixels if value adjustment is needed
                if ( subtract && ( matches = rcssNum.exec( value ) ) &&
                    ( matches[ 3 ] || "px" ) !== "px" ) {

                    elem.style[ dimension ] = value;
                    value = jQuery.css( elem, dimension );
                }

                return setPositiveNumber( elem, value, subtract );
            }
        };
    } );

    jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
        function( elem, computed ) {
            if ( computed ) {
                return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
                    elem.getBoundingClientRect().left -
                    swap( elem, { marginLeft: 0 }, function() {
                        return elem.getBoundingClientRect().left;
                    } )
                ) + "px";
            }
        }
    );

// These hooks are used by animate to expand properties
    jQuery.each( {
        margin: "",
        padding: "",
        border: "Width"
    }, function( prefix, suffix ) {
        jQuery.cssHooks[ prefix + suffix ] = {
            expand: function( value ) {
                var i = 0,
                    expanded = {},

                    // Assumes a single number if not a string
                    parts = typeof value === "string" ? value.split( " " ) : [ value ];

                for ( ; i < 4; i++ ) {
                    expanded[ prefix + cssExpand[ i ] + suffix ] =
                        parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
                }

                return expanded;
            }
        };

        if ( prefix !== "margin" ) {
            jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
        }
    } );

    jQuery.fn.extend( {
        css: function( name, value ) {
            return access( this, function( elem, name, value ) {
                var styles, len,
                    map = {},
                    i = 0;

                if ( Array.isArray( name ) ) {
                    styles = getStyles( elem );
                    len = name.length;

                    for ( ; i < len; i++ ) {
                        map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
                    }

                    return map;
                }

                return value !== undefined ?
                    jQuery.style( elem, name, value ) :
                    jQuery.css( elem, name );
            }, name, value, arguments.length > 1 );
        }
    } );


    function Tween( elem, options, prop, end, easing ) {
        return new Tween.prototype.init( elem, options, prop, end, easing );
    }
    jQuery.Tween = Tween;

    Tween.prototype = {
        constructor: Tween,
        init: function( elem, options, prop, end, easing, unit ) {
            this.elem = elem;
            this.prop = prop;
            this.easing = easing || jQuery.easing._default;
            this.options = options;
            this.start = this.now = this.cur();
            this.end = end;
            this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
        },
        cur: function() {
            var hooks = Tween.propHooks[ this.prop ];

            return hooks && hooks.get ?
                hooks.get( this ) :
                Tween.propHooks._default.get( this );
        },
        run: function( percent ) {
            var eased,
                hooks = Tween.propHooks[ this.prop ];

            if ( this.options.duration ) {
                this.pos = eased = jQuery.easing[ this.easing ](
                    percent, this.options.duration * percent, 0, 1, this.options.duration
                );
            } else {
                this.pos = eased = percent;
            }
            this.now = ( this.end - this.start ) * eased + this.start;

            if ( this.options.step ) {
                this.options.step.call( this.elem, this.now, this );
            }

            if ( hooks && hooks.set ) {
                hooks.set( this );
            } else {
                Tween.propHooks._default.set( this );
            }
            return this;
        }
    };

    Tween.prototype.init.prototype = Tween.prototype;

    Tween.propHooks = {
        _default: {
            get: function( tween ) {
                var result;

                // Use a property on the element directly when it is not a DOM element,
                // or when there is no matching style property that exists.
                if ( tween.elem.nodeType !== 1 ||
                    tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
                    return tween.elem[ tween.prop ];
                }

                // Passing an empty string as a 3rd parameter to .css will automatically
                // attempt a parseFloat and fallback to a string if the parse fails.
                // Simple values such as "10px" are parsed to Float;
                // complex values such as "rotate(1rad)" are returned as-is.
                result = jQuery.css( tween.elem, tween.prop, "" );

                // Empty strings, null, undefined and "auto" are converted to 0.
                return !result || result === "auto" ? 0 : result;
            },
            set: function( tween ) {

                // Use step hook for back compat.
                // Use cssHook if its there.
                // Use .style if available and use plain properties where available.
                if ( jQuery.fx.step[ tween.prop ] ) {
                    jQuery.fx.step[ tween.prop ]( tween );
                } else if ( tween.elem.nodeType === 1 && (
                    jQuery.cssHooks[ tween.prop ] ||
                    tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
                    jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
                } else {
                    tween.elem[ tween.prop ] = tween.now;
                }
            }
        }
    };

// Support: IE <=9 only
// Panic based approach to setting things on disconnected nodes
    Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
        set: function( tween ) {
            if ( tween.elem.nodeType && tween.elem.parentNode ) {
                tween.elem[ tween.prop ] = tween.now;
            }
        }
    };

    jQuery.easing = {
        linear: function( p ) {
            return p;
        },
        swing: function( p ) {
            return 0.5 - Math.cos( p * Math.PI ) / 2;
        },
        _default: "swing"
    };

    jQuery.fx = Tween.prototype.init;

// Back compat <1.8 extension point
    jQuery.fx.step = {};




    var
        fxNow, inProgress,
        rfxtypes = /^(?:toggle|show|hide)$/,
        rrun = /queueHooks$/;

    function schedule() {
        if ( inProgress ) {
            if ( document.hidden === false && window.requestAnimationFrame ) {
                window.requestAnimationFrame( schedule );
            } else {
                window.setTimeout( schedule, jQuery.fx.interval );
            }

            jQuery.fx.tick();
        }
    }

// Animations created synchronously will run synchronously
    function createFxNow() {
        window.setTimeout( function() {
            fxNow = undefined;
        } );
        return ( fxNow = Date.now() );
    }

// Generate parameters to create a standard animation
    function genFx( type, includeWidth ) {
        var which,
            i = 0,
            attrs = { height: type };

        // If we include width, step value is 1 to do all cssExpand values,
        // otherwise step value is 2 to skip over Left and Right
        includeWidth = includeWidth ? 1 : 0;
        for ( ; i < 4; i += 2 - includeWidth ) {
            which = cssExpand[ i ];
            attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
        }

        if ( includeWidth ) {
            attrs.opacity = attrs.width = type;
        }

        return attrs;
    }

    function createTween( value, prop, animation ) {
        var tween,
            collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
            index = 0,
            length = collection.length;
        for ( ; index < length; index++ ) {
            if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {

                // We're done with this property
                return tween;
            }
        }
    }

    function defaultPrefilter( elem, props, opts ) {
        var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
            isBox = "width" in props || "height" in props,
            anim = this,
            orig = {},
            style = elem.style,
            hidden = elem.nodeType && isHiddenWithinTree( elem ),
            dataShow = dataPriv.get( elem, "fxshow" );

        // Queue-skipping animations hijack the fx hooks
        if ( !opts.queue ) {
            hooks = jQuery._queueHooks( elem, "fx" );
            if ( hooks.unqueued == null ) {
                hooks.unqueued = 0;
                oldfire = hooks.empty.fire;
                hooks.empty.fire = function() {
                    if ( !hooks.unqueued ) {
                        oldfire();
                    }
                };
            }
            hooks.unqueued++;

            anim.always( function() {

                // Ensure the complete handler is called before this completes
                anim.always( function() {
                    hooks.unqueued--;
                    if ( !jQuery.queue( elem, "fx" ).length ) {
                        hooks.empty.fire();
                    }
                } );
            } );
        }

        // Detect show/hide animations
        for ( prop in props ) {
            value = props[ prop ];
            if ( rfxtypes.test( value ) ) {
                delete props[ prop ];
                toggle = toggle || value === "toggle";
                if ( value === ( hidden ? "hide" : "show" ) ) {

                    // Pretend to be hidden if this is a "show" and
                    // there is still data from a stopped show/hide
                    if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
                        hidden = true;

                        // Ignore all other no-op show/hide data
                    } else {
                        continue;
                    }
                }
                orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
            }
        }

        // Bail out if this is a no-op like .hide().hide()
        propTween = !jQuery.isEmptyObject( props );
        if ( !propTween && jQuery.isEmptyObject( orig ) ) {
            return;
        }

        // Restrict "overflow" and "display" styles during box animations
        if ( isBox && elem.nodeType === 1 ) {

            // Support: IE <=9 - 11, Edge 12 - 15
            // Record all 3 overflow attributes because IE does not infer the shorthand
            // from identically-valued overflowX and overflowY and Edge just mirrors
            // the overflowX value there.
            opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];

            // Identify a display type, preferring old show/hide data over the CSS cascade
            restoreDisplay = dataShow && dataShow.display;
            if ( restoreDisplay == null ) {
                restoreDisplay = dataPriv.get( elem, "display" );
            }
            display = jQuery.css( elem, "display" );
            if ( display === "none" ) {
                if ( restoreDisplay ) {
                    display = restoreDisplay;
                } else {

                    // Get nonempty value(s) by temporarily forcing visibility
                    showHide( [ elem ], true );
                    restoreDisplay = elem.style.display || restoreDisplay;
                    display = jQuery.css( elem, "display" );
                    showHide( [ elem ] );
                }
            }

            // Animate inline elements as inline-block
            if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
                if ( jQuery.css( elem, "float" ) === "none" ) {

                    // Restore the original display value at the end of pure show/hide animations
                    if ( !propTween ) {
                        anim.done( function() {
                            style.display = restoreDisplay;
                        } );
                        if ( restoreDisplay == null ) {
                            display = style.display;
                            restoreDisplay = display === "none" ? "" : display;
                        }
                    }
                    style.display = "inline-block";
                }
            }
        }

        if ( opts.overflow ) {
            style.overflow = "hidden";
            anim.always( function() {
                style.overflow = opts.overflow[ 0 ];
                style.overflowX = opts.overflow[ 1 ];
                style.overflowY = opts.overflow[ 2 ];
            } );
        }

        // Implement show/hide animations
        propTween = false;
        for ( prop in orig ) {

            // General show/hide setup for this element animation
            if ( !propTween ) {
                if ( dataShow ) {
                    if ( "hidden" in dataShow ) {
                        hidden = dataShow.hidden;
                    }
                } else {
                    dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
                }

                // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
                if ( toggle ) {
                    dataShow.hidden = !hidden;
                }

                // Show elements before animating them
                if ( hidden ) {
                    showHide( [ elem ], true );
                }

                /* eslint-disable no-loop-func */

                anim.done( function() {

                    /* eslint-enable no-loop-func */

                    // The final step of a "hide" animation is actually hiding the element
                    if ( !hidden ) {
                        showHide( [ elem ] );
                    }
                    dataPriv.remove( elem, "fxshow" );
                    for ( prop in orig ) {
                        jQuery.style( elem, prop, orig[ prop ] );
                    }
                } );
            }

            // Per-property setup
            propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
            if ( !( prop in dataShow ) ) {
                dataShow[ prop ] = propTween.start;
                if ( hidden ) {
                    propTween.end = propTween.start;
                    propTween.start = 0;
                }
            }
        }
    }

    function propFilter( props, specialEasing ) {
        var index, name, easing, value, hooks;

        // camelCase, specialEasing and expand cssHook pass
        for ( index in props ) {
            name = camelCase( index );
            easing = specialEasing[ name ];
            value = props[ index ];
            if ( Array.isArray( value ) ) {
                easing = value[ 1 ];
                value = props[ index ] = value[ 0 ];
            }

            if ( index !== name ) {
                props[ name ] = value;
                delete props[ index ];
            }

            hooks = jQuery.cssHooks[ name ];
            if ( hooks && "expand" in hooks ) {
                value = hooks.expand( value );
                delete props[ name ];

                // Not quite $.extend, this won't overwrite existing keys.
                // Reusing 'index' because we have the correct "name"
                for ( index in value ) {
                    if ( !( index in props ) ) {
                        props[ index ] = value[ index ];
                        specialEasing[ index ] = easing;
                    }
                }
            } else {
                specialEasing[ name ] = easing;
            }
        }
    }

    function Animation( elem, properties, options ) {
        var result,
            stopped,
            index = 0,
            length = Animation.prefilters.length,
            deferred = jQuery.Deferred().always( function() {

                // Don't match elem in the :animated selector
                delete tick.elem;
            } ),
            tick = function() {
                if ( stopped ) {
                    return false;
                }
                var currentTime = fxNow || createFxNow(),
                    remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),

                    // Support: Android 2.3 only
                    // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
                    temp = remaining / animation.duration || 0,
                    percent = 1 - temp,
                    index = 0,
                    length = animation.tweens.length;

                for ( ; index < length; index++ ) {
                    animation.tweens[ index ].run( percent );
                }

                deferred.notifyWith( elem, [ animation, percent, remaining ] );

                // If there's more to do, yield
                if ( percent < 1 && length ) {
                    return remaining;
                }

                // If this was an empty animation, synthesize a final progress notification
                if ( !length ) {
                    deferred.notifyWith( elem, [ animation, 1, 0 ] );
                }

                // Resolve the animation and report its conclusion
                deferred.resolveWith( elem, [ animation ] );
                return false;
            },
            animation = deferred.promise( {
                elem: elem,
                props: jQuery.extend( {}, properties ),
                opts: jQuery.extend( true, {
                    specialEasing: {},
                    easing: jQuery.easing._default
                }, options ),
                originalProperties: properties,
                originalOptions: options,
                startTime: fxNow || createFxNow(),
                duration: options.duration,
                tweens: [],
                createTween: function( prop, end ) {
                    var tween = jQuery.Tween( elem, animation.opts, prop, end,
                        animation.opts.specialEasing[ prop ] || animation.opts.easing );
                    animation.tweens.push( tween );
                    return tween;
                },
                stop: function( gotoEnd ) {
                    var index = 0,

                        // If we are going to the end, we want to run all the tweens
                        // otherwise we skip this part
                        length = gotoEnd ? animation.tweens.length : 0;
                    if ( stopped ) {
                        return this;
                    }
                    stopped = true;
                    for ( ; index < length; index++ ) {
                        animation.tweens[ index ].run( 1 );
                    }

                    // Resolve when we played the last frame; otherwise, reject
                    if ( gotoEnd ) {
                        deferred.notifyWith( elem, [ animation, 1, 0 ] );
                        deferred.resolveWith( elem, [ animation, gotoEnd ] );
                    } else {
                        deferred.rejectWith( elem, [ animation, gotoEnd ] );
                    }
                    return this;
                }
            } ),
            props = animation.props;

        propFilter( props, animation.opts.specialEasing );

        for ( ; index < length; index++ ) {
            result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
            if ( result ) {
                if ( isFunction( result.stop ) ) {
                    jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
                        result.stop.bind( result );
                }
                return result;
            }
        }

        jQuery.map( props, createTween, animation );

        if ( isFunction( animation.opts.start ) ) {
            animation.opts.start.call( elem, animation );
        }

        // Attach callbacks from options
        animation
            .progress( animation.opts.progress )
            .done( animation.opts.done, animation.opts.complete )
            .fail( animation.opts.fail )
            .always( animation.opts.always );

        jQuery.fx.timer(
            jQuery.extend( tick, {
                elem: elem,
                anim: animation,
                queue: animation.opts.queue
            } )
        );

        return animation;
    }

    jQuery.Animation = jQuery.extend( Animation, {

        tweeners: {
            "*": [ function( prop, value ) {
                var tween = this.createTween( prop, value );
                adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
                return tween;
            } ]
        },

        tweener: function( props, callback ) {
            if ( isFunction( props ) ) {
                callback = props;
                props = [ "*" ];
            } else {
                props = props.match( rnothtmlwhite );
            }

            var prop,
                index = 0,
                length = props.length;

            for ( ; index < length; index++ ) {
                prop = props[ index ];
                Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
                Animation.tweeners[ prop ].unshift( callback );
            }
        },

        prefilters: [ defaultPrefilter ],

        prefilter: function( callback, prepend ) {
            if ( prepend ) {
                Animation.prefilters.unshift( callback );
            } else {
                Animation.prefilters.push( callback );
            }
        }
    } );

    jQuery.speed = function( speed, easing, fn ) {
        var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
            complete: fn || !fn && easing ||
                isFunction( speed ) && speed,
            duration: speed,
            easing: fn && easing || easing && !isFunction( easing ) && easing
        };

        // Go to the end state if fx are off
        if ( jQuery.fx.off ) {
            opt.duration = 0;

        } else {
            if ( typeof opt.duration !== "number" ) {
                if ( opt.duration in jQuery.fx.speeds ) {
                    opt.duration = jQuery.fx.speeds[ opt.duration ];

                } else {
                    opt.duration = jQuery.fx.speeds._default;
                }
            }
        }

        // Normalize opt.queue - true/undefined/null -> "fx"
        if ( opt.queue == null || opt.queue === true ) {
            opt.queue = "fx";
        }

        // Queueing
        opt.old = opt.complete;

        opt.complete = function() {
            if ( isFunction( opt.old ) ) {
                opt.old.call( this );
            }

            if ( opt.queue ) {
                jQuery.dequeue( this, opt.queue );
            }
        };

        return opt;
    };

    jQuery.fn.extend( {
        fadeTo: function( speed, to, easing, callback ) {

            // Show any hidden elements after setting opacity to 0
            return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()

                // Animate to the value specified
                .end().animate( { opacity: to }, speed, easing, callback );
        },
        animate: function( prop, speed, easing, callback ) {
            var empty = jQuery.isEmptyObject( prop ),
                optall = jQuery.speed( speed, easing, callback ),
                doAnimation = function() {

                    // Operate on a copy of prop so per-property easing won't be lost
                    var anim = Animation( this, jQuery.extend( {}, prop ), optall );

                    // Empty animations, or finishing resolves immediately
                    if ( empty || dataPriv.get( this, "finish" ) ) {
                        anim.stop( true );
                    }
                };

            doAnimation.finish = doAnimation;

            return empty || optall.queue === false ?
                this.each( doAnimation ) :
                this.queue( optall.queue, doAnimation );
        },
        stop: function( type, clearQueue, gotoEnd ) {
            var stopQueue = function( hooks ) {
                var stop = hooks.stop;
                delete hooks.stop;
                stop( gotoEnd );
            };

            if ( typeof type !== "string" ) {
                gotoEnd = clearQueue;
                clearQueue = type;
                type = undefined;
            }
            if ( clearQueue ) {
                this.queue( type || "fx", [] );
            }

            return this.each( function() {
                var dequeue = true,
                    index = type != null && type + "queueHooks",
                    timers = jQuery.timers,
                    data = dataPriv.get( this );

                if ( index ) {
                    if ( data[ index ] && data[ index ].stop ) {
                        stopQueue( data[ index ] );
                    }
                } else {
                    for ( index in data ) {
                        if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
                            stopQueue( data[ index ] );
                        }
                    }
                }

                for ( index = timers.length; index--; ) {
                    if ( timers[ index ].elem === this &&
                        ( type == null || timers[ index ].queue === type ) ) {

                        timers[ index ].anim.stop( gotoEnd );
                        dequeue = false;
                        timers.splice( index, 1 );
                    }
                }

                // Start the next in the queue if the last step wasn't forced.
                // Timers currently will call their complete callbacks, which
                // will dequeue but only if they were gotoEnd.
                if ( dequeue || !gotoEnd ) {
                    jQuery.dequeue( this, type );
                }
            } );
        },
        finish: function( type ) {
            if ( type !== false ) {
                type = type || "fx";
            }
            return this.each( function() {
                var index,
                    data = dataPriv.get( this ),
                    queue = data[ type + "queue" ],
                    hooks = data[ type + "queueHooks" ],
                    timers = jQuery.timers,
                    length = queue ? queue.length : 0;

                // Enable finishing flag on private data
                data.finish = true;

                // Empty the queue first
                jQuery.queue( this, type, [] );

                if ( hooks && hooks.stop ) {
                    hooks.stop.call( this, true );
                }

                // Look for any active animations, and finish them
                for ( index = timers.length; index--; ) {
                    if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
                        timers[ index ].anim.stop( true );
                        timers.splice( index, 1 );
                    }
                }

                // Look for any animations in the old queue and finish them
                for ( index = 0; index < length; index++ ) {
                    if ( queue[ index ] && queue[ index ].finish ) {
                        queue[ index ].finish.call( this );
                    }
                }

                // Turn off finishing flag
                delete data.finish;
            } );
        }
    } );

    jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
        var cssFn = jQuery.fn[ name ];
        jQuery.fn[ name ] = function( speed, easing, callback ) {
            return speed == null || typeof speed === "boolean" ?
                cssFn.apply( this, arguments ) :
                this.animate( genFx( name, true ), speed, easing, callback );
        };
    } );

// Generate shortcuts for custom animations
    jQuery.each( {
        slideDown: genFx( "show" ),
        slideUp: genFx( "hide" ),
        slideToggle: genFx( "toggle" ),
        fadeIn: { opacity: "show" },
        fadeOut: { opacity: "hide" },
        fadeToggle: { opacity: "toggle" }
    }, function( name, props ) {
        jQuery.fn[ name ] = function( speed, easing, callback ) {
            return this.animate( props, speed, easing, callback );
        };
    } );

    jQuery.timers = [];
    jQuery.fx.tick = function() {
        var timer,
            i = 0,
            timers = jQuery.timers;

        fxNow = Date.now();

        for ( ; i < timers.length; i++ ) {
            timer = timers[ i ];

            // Run the timer and safely remove it when done (allowing for external removal)
            if ( !timer() && timers[ i ] === timer ) {
                timers.splice( i--, 1 );
            }
        }

        if ( !timers.length ) {
            jQuery.fx.stop();
        }
        fxNow = undefined;
    };

    jQuery.fx.timer = function( timer ) {
        jQuery.timers.push( timer );
        jQuery.fx.start();
    };

    jQuery.fx.interval = 13;
    jQuery.fx.start = function() {
        if ( inProgress ) {
            return;
        }

        inProgress = true;
        schedule();
    };

    jQuery.fx.stop = function() {
        inProgress = null;
    };

    jQuery.fx.speeds = {
        slow: 600,
        fast: 200,

        // Default speed
        _default: 400
    };


// Based off of the plugin by Clint Helfers, with permission.
// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
    jQuery.fn.delay = function( time, type ) {
        time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
        type = type || "fx";

        return this.queue( type, function( next, hooks ) {
            var timeout = window.setTimeout( next, time );
            hooks.stop = function() {
                window.clearTimeout( timeout );
            };
        } );
    };


    ( function() {
        var input = document.createElement( "input" ),
            select = document.createElement( "select" ),
            opt = select.appendChild( document.createElement( "option" ) );

        input.type = "checkbox";

        // Support: Android <=4.3 only
        // Default value for a checkbox should be "on"
        support.checkOn = input.value !== "";

        // Support: IE <=11 only
        // Must access selectedIndex to make default options select
        support.optSelected = opt.selected;

        // Support: IE <=11 only
        // An input loses its value after becoming a radio
        input = document.createElement( "input" );
        input.value = "t";
        input.type = "radio";
        support.radioValue = input.value === "t";
    } )();


    var boolHook,
        attrHandle = jQuery.expr.attrHandle;

    jQuery.fn.extend( {
        attr: function( name, value ) {
            return access( this, jQuery.attr, name, value, arguments.length > 1 );
        },

        removeAttr: function( name ) {
            return this.each( function() {
                jQuery.removeAttr( this, name );
            } );
        }
    } );

    jQuery.extend( {
        attr: function( elem, name, value ) {
            var ret, hooks,
                nType = elem.nodeType;

            // Don't get/set attributes on text, comment and attribute nodes
            if ( nType === 3 || nType === 8 || nType === 2 ) {
                return;
            }

            // Fallback to prop when attributes are not supported
            if ( typeof elem.getAttribute === "undefined" ) {
                return jQuery.prop( elem, name, value );
            }

            // Attribute hooks are determined by the lowercase version
            // Grab necessary hook if one is defined
            if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
                hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
                    ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
            }

            if ( value !== undefined ) {
                if ( value === null ) {
                    jQuery.removeAttr( elem, name );
                    return;
                }

                if ( hooks && "set" in hooks &&
                    ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
                    return ret;
                }

                elem.setAttribute( name, value + "" );
                return value;
            }

            if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
                return ret;
            }

            ret = jQuery.find.attr( elem, name );

            // Non-existent attributes return null, we normalize to undefined
            return ret == null ? undefined : ret;
        },

        attrHooks: {
            type: {
                set: function( elem, value ) {
                    if ( !support.radioValue && value === "radio" &&
                        nodeName( elem, "input" ) ) {
                        var val = elem.value;
                        elem.setAttribute( "type", value );
                        if ( val ) {
                            elem.value = val;
                        }
                        return value;
                    }
                }
            }
        },

        removeAttr: function( elem, value ) {
            var name,
                i = 0,

                // Attribute names can contain non-HTML whitespace characters
                // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
                attrNames = value && value.match( rnothtmlwhite );

            if ( attrNames && elem.nodeType === 1 ) {
                while ( ( name = attrNames[ i++ ] ) ) {
                    elem.removeAttribute( name );
                }
            }
        }
    } );

// Hooks for boolean attributes
    boolHook = {
        set: function( elem, value, name ) {
            if ( value === false ) {

                // Remove boolean attributes when set to false
                jQuery.removeAttr( elem, name );
            } else {
                elem.setAttribute( name, name );
            }
            return name;
        }
    };

    jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
        var getter = attrHandle[ name ] || jQuery.find.attr;

        attrHandle[ name ] = function( elem, name, isXML ) {
            var ret, handle,
                lowercaseName = name.toLowerCase();

            if ( !isXML ) {

                // Avoid an infinite loop by temporarily removing this function from the getter
                handle = attrHandle[ lowercaseName ];
                attrHandle[ lowercaseName ] = ret;
                ret = getter( elem, name, isXML ) != null ?
                    lowercaseName :
                    null;
                attrHandle[ lowercaseName ] = handle;
            }
            return ret;
        };
    } );




    var rfocusable = /^(?:input|select|textarea|button)$/i,
        rclickable = /^(?:a|area)$/i;

    jQuery.fn.extend( {
        prop: function( name, value ) {
            return access( this, jQuery.prop, name, value, arguments.length > 1 );
        },

        removeProp: function( name ) {
            return this.each( function() {
                delete this[ jQuery.propFix[ name ] || name ];
            } );
        }
    } );

    jQuery.extend( {
        prop: function( elem, name, value ) {
            var ret, hooks,
                nType = elem.nodeType;

            // Don't get/set properties on text, comment and attribute nodes
            if ( nType === 3 || nType === 8 || nType === 2 ) {
                return;
            }

            if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {

                // Fix name and attach hooks
                name = jQuery.propFix[ name ] || name;
                hooks = jQuery.propHooks[ name ];
            }

            if ( value !== undefined ) {
                if ( hooks && "set" in hooks &&
                    ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
                    return ret;
                }

                return ( elem[ name ] = value );
            }

            if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
                return ret;
            }

            return elem[ name ];
        },

        propHooks: {
            tabIndex: {
                get: function( elem ) {

                    // Support: IE <=9 - 11 only
                    // elem.tabIndex doesn't always return the
                    // correct value when it hasn't been explicitly set
                    // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
                    // Use proper attribute retrieval(#12072)
                    var tabindex = jQuery.find.attr( elem, "tabindex" );

                    if ( tabindex ) {
                        return parseInt( tabindex, 10 );
                    }

                    if (
                        rfocusable.test( elem.nodeName ) ||
                        rclickable.test( elem.nodeName ) &&
                        elem.href
                    ) {
                        return 0;
                    }

                    return -1;
                }
            }
        },

        propFix: {
            "for": "htmlFor",
            "class": "className"
        }
    } );

// Support: IE <=11 only
// Accessing the selectedIndex property
// forces the browser to respect setting selected
// on the option
// The getter ensures a default option is selected
// when in an optgroup
// eslint rule "no-unused-expressions" is disabled for this code
// since it considers such accessions noop
    if ( !support.optSelected ) {
        jQuery.propHooks.selected = {
            get: function( elem ) {

                /* eslint no-unused-expressions: "off" */

                var parent = elem.parentNode;
                if ( parent && parent.parentNode ) {
                    parent.parentNode.selectedIndex;
                }
                return null;
            },
            set: function( elem ) {

                /* eslint no-unused-expressions: "off" */

                var parent = elem.parentNode;
                if ( parent ) {
                    parent.selectedIndex;

                    if ( parent.parentNode ) {
                        parent.parentNode.selectedIndex;
                    }
                }
            }
        };
    }

    jQuery.each( [
        "tabIndex",
        "readOnly",
        "maxLength",
        "cellSpacing",
        "cellPadding",
        "rowSpan",
        "colSpan",
        "useMap",
        "frameBorder",
        "contentEditable"
    ], function() {
        jQuery.propFix[ this.toLowerCase() ] = this;
    } );




    // Strip and collapse whitespace according to HTML spec
    // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
    function stripAndCollapse( value ) {
        var tokens = value.match( rnothtmlwhite ) || [];
        return tokens.join( " " );
    }


    function getClass( elem ) {
        return elem.getAttribute && elem.getAttribute( "class" ) || "";
    }

    function classesToArray( value ) {
        if ( Array.isArray( value ) ) {
            return value;
        }
        if ( typeof value === "string" ) {
            return value.match( rnothtmlwhite ) || [];
        }
        return [];
    }

    jQuery.fn.extend( {
        addClass: function( value ) {
            var classes, elem, cur, curValue, clazz, j, finalValue,
                i = 0;

            if ( isFunction( value ) ) {
                return this.each( function( j ) {
                    jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
                } );
            }

            classes = classesToArray( value );

            if ( classes.length ) {
                while ( ( elem = this[ i++ ] ) ) {
                    curValue = getClass( elem );
                    cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

                    if ( cur ) {
                        j = 0;
                        while ( ( clazz = classes[ j++ ] ) ) {
                            if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
                                cur += clazz + " ";
                            }
                        }

                        // Only assign if different to avoid unneeded rendering.
                        finalValue = stripAndCollapse( cur );
                        if ( curValue !== finalValue ) {
                            elem.setAttribute( "class", finalValue );
                        }
                    }
                }
            }

            return this;
        },

        removeClass: function( value ) {
            var classes, elem, cur, curValue, clazz, j, finalValue,
                i = 0;

            if ( isFunction( value ) ) {
                return this.each( function( j ) {
                    jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
                } );
            }

            if ( !arguments.length ) {
                return this.attr( "class", "" );
            }

            classes = classesToArray( value );

            if ( classes.length ) {
                while ( ( elem = this[ i++ ] ) ) {
                    curValue = getClass( elem );

                    // This expression is here for better compressibility (see addClass)
                    cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

                    if ( cur ) {
                        j = 0;
                        while ( ( clazz = classes[ j++ ] ) ) {

                            // Remove *all* instances
                            while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
                                cur = cur.replace( " " + clazz + " ", " " );
                            }
                        }

                        // Only assign if different to avoid unneeded rendering.
                        finalValue = stripAndCollapse( cur );
                        if ( curValue !== finalValue ) {
                            elem.setAttribute( "class", finalValue );
                        }
                    }
                }
            }

            return this;
        },

        toggleClass: function( value, stateVal ) {
            var type = typeof value,
                isValidValue = type === "string" || Array.isArray( value );

            if ( typeof stateVal === "boolean" && isValidValue ) {
                return stateVal ? this.addClass( value ) : this.removeClass( value );
            }

            if ( isFunction( value ) ) {
                return this.each( function( i ) {
                    jQuery( this ).toggleClass(
                        value.call( this, i, getClass( this ), stateVal ),
                        stateVal
                    );
                } );
            }

            return this.each( function() {
                var className, i, self, classNames;

                if ( isValidValue ) {

                    // Toggle individual class names
                    i = 0;
                    self = jQuery( this );
                    classNames = classesToArray( value );

                    while ( ( className = classNames[ i++ ] ) ) {

                        // Check each className given, space separated list
                        if ( self.hasClass( className ) ) {
                            self.removeClass( className );
                        } else {
                            self.addClass( className );
                        }
                    }

                    // Toggle whole class name
                } else if ( value === undefined || type === "boolean" ) {
                    className = getClass( this );
                    if ( className ) {

                        // Store className if set
                        dataPriv.set( this, "__className__", className );
                    }

                    // If the element has a class name or if we're passed `false`,
                    // then remove the whole classname (if there was one, the above saved it).
                    // Otherwise bring back whatever was previously saved (if anything),
                    // falling back to the empty string if nothing was stored.
                    if ( this.setAttribute ) {
                        this.setAttribute( "class",
                            className || value === false ?
                                "" :
                                dataPriv.get( this, "__className__" ) || ""
                        );
                    }
                }
            } );
        },

        hasClass: function( selector ) {
            var className, elem,
                i = 0;

            className = " " + selector + " ";
            while ( ( elem = this[ i++ ] ) ) {
                if ( elem.nodeType === 1 &&
                    ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
                    return true;
                }
            }

            return false;
        }
    } );




    var rreturn = /\r/g;

    jQuery.fn.extend( {
        val: function( value ) {
            var hooks, ret, valueIsFunction,
                elem = this[ 0 ];

            if ( !arguments.length ) {
                if ( elem ) {
                    hooks = jQuery.valHooks[ elem.type ] ||
                        jQuery.valHooks[ elem.nodeName.toLowerCase() ];

                    if ( hooks &&
                        "get" in hooks &&
                        ( ret = hooks.get( elem, "value" ) ) !== undefined
                    ) {
                        return ret;
                    }

                    ret = elem.value;

                    // Handle most common string cases
                    if ( typeof ret === "string" ) {
                        return ret.replace( rreturn, "" );
                    }

                    // Handle cases where value is null/undef or number
                    return ret == null ? "" : ret;
                }

                return;
            }

            valueIsFunction = isFunction( value );

            return this.each( function( i ) {
                var val;

                if ( this.nodeType !== 1 ) {
                    return;
                }

                if ( valueIsFunction ) {
                    val = value.call( this, i, jQuery( this ).val() );
                } else {
                    val = value;
                }

                // Treat null/undefined as ""; convert numbers to string
                if ( val == null ) {
                    val = "";

                } else if ( typeof val === "number" ) {
                    val += "";

                } else if ( Array.isArray( val ) ) {
                    val = jQuery.map( val, function( value ) {
                        return value == null ? "" : value + "";
                    } );
                }

                hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

                // If set returns undefined, fall back to normal setting
                if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
                    this.value = val;
                }
            } );
        }
    } );

    jQuery.extend( {
        valHooks: {
            option: {
                get: function( elem ) {

                    var val = jQuery.find.attr( elem, "value" );
                    return val != null ?
                        val :

                        // Support: IE <=10 - 11 only
                        // option.text throws exceptions (#14686, #14858)
                        // Strip and collapse whitespace
                        // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
                        stripAndCollapse( jQuery.text( elem ) );
                }
            },
            select: {
                get: function( elem ) {
                    var value, option, i,
                        options = elem.options,
                        index = elem.selectedIndex,
                        one = elem.type === "select-one",
                        values = one ? null : [],
                        max = one ? index + 1 : options.length;

                    if ( index < 0 ) {
                        i = max;

                    } else {
                        i = one ? index : 0;
                    }

                    // Loop through all the selected options
                    for ( ; i < max; i++ ) {
                        option = options[ i ];

                        // Support: IE <=9 only
                        // IE8-9 doesn't update selected after form reset (#2551)
                        if ( ( option.selected || i === index ) &&

                            // Don't return options that are disabled or in a disabled optgroup
                            !option.disabled &&
                            ( !option.parentNode.disabled ||
                                !nodeName( option.parentNode, "optgroup" ) ) ) {

                            // Get the specific value for the option
                            value = jQuery( option ).val();

                            // We don't need an array for one selects
                            if ( one ) {
                                return value;
                            }

                            // Multi-Selects return an array
                            values.push( value );
                        }
                    }

                    return values;
                },

                set: function( elem, value ) {
                    var optionSet, option,
                        options = elem.options,
                        values = jQuery.makeArray( value ),
                        i = options.length;

                    while ( i-- ) {
                        option = options[ i ];

                        /* eslint-disable no-cond-assign */

                        if ( option.selected =
                            jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
                        ) {
                            optionSet = true;
                        }

                        /* eslint-enable no-cond-assign */
                    }

                    // Force browsers to behave consistently when non-matching value is set
                    if ( !optionSet ) {
                        elem.selectedIndex = -1;
                    }
                    return values;
                }
            }
        }
    } );

// Radios and checkboxes getter/setter
    jQuery.each( [ "radio", "checkbox" ], function() {
        jQuery.valHooks[ this ] = {
            set: function( elem, value ) {
                if ( Array.isArray( value ) ) {
                    return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
                }
            }
        };
        if ( !support.checkOn ) {
            jQuery.valHooks[ this ].get = function( elem ) {
                return elem.getAttribute( "value" ) === null ? "on" : elem.value;
            };
        }
    } );




// Return jQuery for attributes-only inclusion


    support.focusin = "onfocusin" in window;


    var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
        stopPropagationCallback = function( e ) {
            e.stopPropagation();
        };

    jQuery.extend( jQuery.event, {

        trigger: function( event, data, elem, onlyHandlers ) {

            var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
                eventPath = [ elem || document ],
                type = hasOwn.call( event, "type" ) ? event.type : event,
                namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];

            cur = lastElement = tmp = elem = elem || document;

            // Don't do events on text and comment nodes
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                return;
            }

            // focus/blur morphs to focusin/out; ensure we're not firing them right now
            if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
                return;
            }

            if ( type.indexOf( "." ) > -1 ) {

                // Namespaced trigger; create a regexp to match event type in handle()
                namespaces = type.split( "." );
                type = namespaces.shift();
                namespaces.sort();
            }
            ontype = type.indexOf( ":" ) < 0 && "on" + type;

            // Caller can pass in a jQuery.Event object, Object, or just an event type string
            event = event[ jQuery.expando ] ?
                event :
                new jQuery.Event( type, typeof event === "object" && event );

            // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
            event.isTrigger = onlyHandlers ? 2 : 3;
            event.namespace = namespaces.join( "." );
            event.rnamespace = event.namespace ?
                new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
                null;

            // Clean up the event in case it is being reused
            event.result = undefined;
            if ( !event.target ) {
                event.target = elem;
            }

            // Clone any incoming data and prepend the event, creating the handler arg list
            data = data == null ?
                [ event ] :
                jQuery.makeArray( data, [ event ] );

            // Allow special events to draw outside the lines
            special = jQuery.event.special[ type ] || {};
            if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
                return;
            }

            // Determine event propagation path in advance, per W3C events spec (#9951)
            // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
            if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {

                bubbleType = special.delegateType || type;
                if ( !rfocusMorph.test( bubbleType + type ) ) {
                    cur = cur.parentNode;
                }
                for ( ; cur; cur = cur.parentNode ) {
                    eventPath.push( cur );
                    tmp = cur;
                }

                // Only add window if we got to document (e.g., not plain obj or detached DOM)
                if ( tmp === ( elem.ownerDocument || document ) ) {
                    eventPath.push( tmp.defaultView || tmp.parentWindow || window );
                }
            }

            // Fire handlers on the event path
            i = 0;
            while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
                lastElement = cur;
                event.type = i > 1 ?
                    bubbleType :
                    special.bindType || type;

                // jQuery handler
                handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] &&
                    dataPriv.get( cur, "handle" );
                if ( handle ) {
                    handle.apply( cur, data );
                }

                // Native handler
                handle = ontype && cur[ ontype ];
                if ( handle && handle.apply && acceptData( cur ) ) {
                    event.result = handle.apply( cur, data );
                    if ( event.result === false ) {
                        event.preventDefault();
                    }
                }
            }
            event.type = type;

            // If nobody prevented the default action, do it now
            if ( !onlyHandlers && !event.isDefaultPrevented() ) {

                if ( ( !special._default ||
                        special._default.apply( eventPath.pop(), data ) === false ) &&
                    acceptData( elem ) ) {

                    // Call a native DOM method on the target with the same name as the event.
                    // Don't do default actions on window, that's where global variables be (#6170)
                    if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {

                        // Don't re-trigger an onFOO event when we call its FOO() method
                        tmp = elem[ ontype ];

                        if ( tmp ) {
                            elem[ ontype ] = null;
                        }

                        // Prevent re-triggering of the same event, since we already bubbled it above
                        jQuery.event.triggered = type;

                        if ( event.isPropagationStopped() ) {
                            lastElement.addEventListener( type, stopPropagationCallback );
                        }

                        elem[ type ]();

                        if ( event.isPropagationStopped() ) {
                            lastElement.removeEventListener( type, stopPropagationCallback );
                        }

                        jQuery.event.triggered = undefined;

                        if ( tmp ) {
                            elem[ ontype ] = tmp;
                        }
                    }
                }
            }

            return event.result;
        },

        // Piggyback on a donor event to simulate a different one
        // Used only for `focus(in | out)` events
        simulate: function( type, elem, event ) {
            var e = jQuery.extend(
                new jQuery.Event(),
                event,
                {
                    type: type,
                    isSimulated: true
                }
            );

            jQuery.event.trigger( e, null, elem );
        }

    } );

    jQuery.fn.extend( {

        trigger: function( type, data ) {
            return this.each( function() {
                jQuery.event.trigger( type, data, this );
            } );
        },
        triggerHandler: function( type, data ) {
            var elem = this[ 0 ];
            if ( elem ) {
                return jQuery.event.trigger( type, data, elem, true );
            }
        }
    } );


// Support: Firefox <=44
// Firefox doesn't have focus(in | out) events
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
//
// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
// focus(in | out) events fire after focus & blur events,
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
    if ( !support.focusin ) {
        jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {

            // Attach a single capturing handler on the document while someone wants focusin/focusout
            var handler = function( event ) {
                jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
            };

            jQuery.event.special[ fix ] = {
                setup: function() {

                    // Handle: regular nodes (via `this.ownerDocument`), window
                    // (via `this.document`) & document (via `this`).
                    var doc = this.ownerDocument || this.document || this,
                        attaches = dataPriv.access( doc, fix );

                    if ( !attaches ) {
                        doc.addEventListener( orig, handler, true );
                    }
                    dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
                },
                teardown: function() {
                    var doc = this.ownerDocument || this.document || this,
                        attaches = dataPriv.access( doc, fix ) - 1;

                    if ( !attaches ) {
                        doc.removeEventListener( orig, handler, true );
                        dataPriv.remove( doc, fix );

                    } else {
                        dataPriv.access( doc, fix, attaches );
                    }
                }
            };
        } );
    }
    var location = window.location;

    var nonce = { guid: Date.now() };

    var rquery = ( /\?/ );



// Cross-browser xml parsing
    jQuery.parseXML = function( data ) {
        var xml, parserErrorElem;
        if ( !data || typeof data !== "string" ) {
            return null;
        }

        // Support: IE 9 - 11 only
        // IE throws on parseFromString with invalid input.
        try {
            xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
        } catch ( e ) {}

        parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ];
        if ( !xml || parserErrorElem ) {
            jQuery.error( "Invalid XML: " + (
                parserErrorElem ?
                    jQuery.map( parserErrorElem.childNodes, function( el ) {
                        return el.textContent;
                    } ).join( "\n" ) :
                    data
            ) );
        }
        return xml;
    };


    var
        rbracket = /\[\]$/,
        rCRLF = /\r?\n/g,
        rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
        rsubmittable = /^(?:input|select|textarea|keygen)/i;

    function buildParams( prefix, obj, traditional, add ) {
        var name;

        if ( Array.isArray( obj ) ) {

            // Serialize array item.
            jQuery.each( obj, function( i, v ) {
                if ( traditional || rbracket.test( prefix ) ) {

                    // Treat each array item as a scalar.
                    add( prefix, v );

                } else {

                    // Item is non-scalar (array or object), encode its numeric index.
                    buildParams(
                        prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
                        v,
                        traditional,
                        add
                    );
                }
            } );

        } else if ( !traditional && toType( obj ) === "object" ) {

            // Serialize object item.
            for ( name in obj ) {
                buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
            }

        } else {

            // Serialize scalar item.
            add( prefix, obj );
        }
    }

// Serialize an array of form elements or a set of
// key/values into a query string
    jQuery.param = function( a, traditional ) {
        var prefix,
            s = [],
            add = function( key, valueOrFunction ) {

                // If value is a function, invoke it and use its return value
                var value = isFunction( valueOrFunction ) ?
                    valueOrFunction() :
                    valueOrFunction;

                s[ s.length ] = encodeURIComponent( key ) + "=" +
                    encodeURIComponent( value == null ? "" : value );
            };

        if ( a == null ) {
            return "";
        }

        // If an array was passed in, assume that it is an array of form elements.
        if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {

            // Serialize the form elements
            jQuery.each( a, function() {
                add( this.name, this.value );
            } );

        } else {

            // If traditional, encode the "old" way (the way 1.3.2 or older
            // did it), otherwise encode params recursively.
            for ( prefix in a ) {
                buildParams( prefix, a[ prefix ], traditional, add );
            }
        }

        // Return the resulting serialization
        return s.join( "&" );
    };

    jQuery.fn.extend( {
        serialize: function() {
            return jQuery.param( this.serializeArray() );
        },
        serializeArray: function() {
            return this.map( function() {

                // Can add propHook for "elements" to filter or add form elements
                var elements = jQuery.prop( this, "elements" );
                return elements ? jQuery.makeArray( elements ) : this;
            } ).filter( function() {
                var type = this.type;

                // Use .is( ":disabled" ) so that fieldset[disabled] works
                return this.name && !jQuery( this ).is( ":disabled" ) &&
                    rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
                    ( this.checked || !rcheckableType.test( type ) );
            } ).map( function( _i, elem ) {
                var val = jQuery( this ).val();

                if ( val == null ) {
                    return null;
                }

                if ( Array.isArray( val ) ) {
                    return jQuery.map( val, function( val ) {
                        return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                    } );
                }

                return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
            } ).get();
        }
    } );


    var
        r20 = /%20/g,
        rhash = /#.*$/,
        rantiCache = /([?&])_=[^&]*/,
        rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,

        // #7653, #8125, #8152: local protocol detection
        rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
        rnoContent = /^(?:GET|HEAD)$/,
        rprotocol = /^\/\//,

        /* Prefilters
	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
	 * 2) These are called:
	 *    - BEFORE asking for a transport
	 *    - AFTER param serialization (s.data is a string if s.processData is true)
	 * 3) key is the dataType
	 * 4) the catchall symbol "*" can be used
	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
	 */
        prefilters = {},

        /* Transports bindings
	 * 1) key is the dataType
	 * 2) the catchall symbol "*" can be used
	 * 3) selection will start with transport dataType and THEN go to "*" if needed
	 */
        transports = {},

        // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
        allTypes = "*/".concat( "*" ),

        // Anchor tag for parsing the document origin
        originAnchor = document.createElement( "a" );

    originAnchor.href = location.href;

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    function addToPrefiltersOrTransports( structure ) {

        // dataTypeExpression is optional and defaults to "*"
        return function( dataTypeExpression, func ) {

            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }

            var dataType,
                i = 0,
                dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];

            if ( isFunction( func ) ) {

                // For each dataType in the dataTypeExpression
                while ( ( dataType = dataTypes[ i++ ] ) ) {

                    // Prepend if requested
                    if ( dataType[ 0 ] === "+" ) {
                        dataType = dataType.slice( 1 ) || "*";
                        ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );

                        // Otherwise append
                    } else {
                        ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
                    }
                }
            }
        };
    }

// Base inspection function for prefilters and transports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {

        var inspected = {},
            seekingTransport = ( structure === transports );

        function inspect( dataType ) {
            var selected;
            inspected[ dataType ] = true;
            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
                if ( typeof dataTypeOrTransport === "string" &&
                    !seekingTransport && !inspected[ dataTypeOrTransport ] ) {

                    options.dataTypes.unshift( dataTypeOrTransport );
                    inspect( dataTypeOrTransport );
                    return false;
                } else if ( seekingTransport ) {
                    return !( selected = dataTypeOrTransport );
                }
            } );
            return selected;
        }

        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
    }

// A special extend for ajax options
// that takes "flat" options (not to be deep extended)
// Fixes #9887
    function ajaxExtend( target, src ) {
        var key, deep,
            flatOptions = jQuery.ajaxSettings.flatOptions || {};

        for ( key in src ) {
            if ( src[ key ] !== undefined ) {
                ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
            }
        }
        if ( deep ) {
            jQuery.extend( true, target, deep );
        }

        return target;
    }

    /* Handles responses to an ajax request:
 * - finds the right dataType (mediates between content-type and expected dataType)
 * - returns the corresponding response
 */
    function ajaxHandleResponses( s, jqXHR, responses ) {

        var ct, type, finalDataType, firstDataType,
            contents = s.contents,
            dataTypes = s.dataTypes;

        // Remove auto dataType and get content-type in the process
        while ( dataTypes[ 0 ] === "*" ) {
            dataTypes.shift();
            if ( ct === undefined ) {
                ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
            }
        }

        // Check if we're dealing with a known content-type
        if ( ct ) {
            for ( type in contents ) {
                if ( contents[ type ] && contents[ type ].test( ct ) ) {
                    dataTypes.unshift( type );
                    break;
                }
            }
        }

        // Check to see if we have a response for the expected dataType
        if ( dataTypes[ 0 ] in responses ) {
            finalDataType = dataTypes[ 0 ];
        } else {

            // Try convertible dataTypes
            for ( type in responses ) {
                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
                    finalDataType = type;
                    break;
                }
                if ( !firstDataType ) {
                    firstDataType = type;
                }
            }

            // Or just use first one
            finalDataType = finalDataType || firstDataType;
        }

        // If we found a dataType
        // We add the dataType to the list if needed
        // and return the corresponding response
        if ( finalDataType ) {
            if ( finalDataType !== dataTypes[ 0 ] ) {
                dataTypes.unshift( finalDataType );
            }
            return responses[ finalDataType ];
        }
    }

    /* Chain conversions given the request and the original response
 * Also sets the responseXXX fields on the jqXHR instance
 */
    function ajaxConvert( s, response, jqXHR, isSuccess ) {
        var conv2, current, conv, tmp, prev,
            converters = {},

            // Work with a copy of dataTypes in case we need to modify it for conversion
            dataTypes = s.dataTypes.slice();

        // Create converters map with lowercased keys
        if ( dataTypes[ 1 ] ) {
            for ( conv in s.converters ) {
                converters[ conv.toLowerCase() ] = s.converters[ conv ];
            }
        }

        current = dataTypes.shift();

        // Convert to each sequential dataType
        while ( current ) {

            if ( s.responseFields[ current ] ) {
                jqXHR[ s.responseFields[ current ] ] = response;
            }

            // Apply the dataFilter if provided
            if ( !prev && isSuccess && s.dataFilter ) {
                response = s.dataFilter( response, s.dataType );
            }

            prev = current;
            current = dataTypes.shift();

            if ( current ) {

                // There's only work to do if current dataType is non-auto
                if ( current === "*" ) {

                    current = prev;

                    // Convert response if prev dataType is non-auto and differs from current
                } else if ( prev !== "*" && prev !== current ) {

                    // Seek a direct converter
                    conv = converters[ prev + " " + current ] || converters[ "* " + current ];

                    // If none found, seek a pair
                    if ( !conv ) {
                        for ( conv2 in converters ) {

                            // If conv2 outputs current
                            tmp = conv2.split( " " );
                            if ( tmp[ 1 ] === current ) {

                                // If prev can be converted to accepted input
                                conv = converters[ prev + " " + tmp[ 0 ] ] ||
                                    converters[ "* " + tmp[ 0 ] ];
                                if ( conv ) {

                                    // Condense equivalence converters
                                    if ( conv === true ) {
                                        conv = converters[ conv2 ];

                                        // Otherwise, insert the intermediate dataType
                                    } else if ( converters[ conv2 ] !== true ) {
                                        current = tmp[ 0 ];
                                        dataTypes.unshift( tmp[ 1 ] );
                                    }
                                    break;
                                }
                            }
                        }
                    }

                    // Apply converter (if not an equivalence)
                    if ( conv !== true ) {

                        // Unless errors are allowed to bubble, catch and return them
                        if ( conv && s.throws ) {
                            response = conv( response );
                        } else {
                            try {
                                response = conv( response );
                            } catch ( e ) {
                                return {
                                    state: "parsererror",
                                    error: conv ? e : "No conversion from " + prev + " to " + current
                                };
                            }
                        }
                    }
                }
            }
        }

        return { state: "success", data: response };
    }

    jQuery.extend( {

        // Counter for holding the number of active queries
        active: 0,

        // Last-Modified header cache for next request
        lastModified: {},
        etag: {},

        ajaxSettings: {
            url: location.href,
            type: "GET",
            isLocal: rlocalProtocol.test( location.protocol ),
            global: true,
            processData: true,
            async: true,
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",

            /*
		timeout: 0,
		data: null,
		dataType: null,
		username: null,
		password: null,
		cache: null,
		throws: false,
		traditional: false,
		headers: {},
		*/

            accepts: {
                "*": allTypes,
                text: "text/plain",
                html: "text/html",
                xml: "application/xml, text/xml",
                json: "application/json, text/javascript"
            },

            contents: {
                xml: /\bxml\b/,
                html: /\bhtml/,
                json: /\bjson\b/
            },

            responseFields: {
                xml: "responseXML",
                text: "responseText",
                json: "responseJSON"
            },

            // Data converters
            // Keys separate source (or catchall "*") and destination types with a single space
            converters: {

                // Convert anything to text
                "* text": String,

                // Text to html (true = no transformation)
                "text html": true,

                // Evaluate text as a json expression
                "text json": JSON.parse,

                // Parse text as xml
                "text xml": jQuery.parseXML
            },

            // For options that shouldn't be deep extended:
            // you can add your own custom options here if
            // and when you create one that shouldn't be
            // deep extended (see ajaxExtend)
            flatOptions: {
                url: true,
                context: true
            }
        },

        // Creates a full fledged settings object into target
        // with both ajaxSettings and settings fields.
        // If target is omitted, writes into ajaxSettings.
        ajaxSetup: function( target, settings ) {
            return settings ?

                // Building a settings object
                ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :

                // Extending ajaxSettings
                ajaxExtend( jQuery.ajaxSettings, target );
        },

        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
        ajaxTransport: addToPrefiltersOrTransports( transports ),

        // Main method
        ajax: function( url, options ) {

            // If url is an object, simulate pre-1.5 signature
            if ( typeof url === "object" ) {
                options = url;
                url = undefined;
            }

            // Force options to be an object
            options = options || {};

            var transport,

                // URL without anti-cache param
                cacheURL,

                // Response headers
                responseHeadersString,
                responseHeaders,

                // timeout handle
                timeoutTimer,

                // Url cleanup var
                urlAnchor,

                // Request state (becomes false upon send and true upon completion)
                completed,

                // To know if global events are to be dispatched
                fireGlobals,

                // Loop variable
                i,

                // uncached part of the url
                uncached,

                // Create the final options object
                s = jQuery.ajaxSetup( {}, options ),

                // Callbacks context
                callbackContext = s.context || s,

                // Context for global events is callbackContext if it is a DOM node or jQuery collection
                globalEventContext = s.context &&
                ( callbackContext.nodeType || callbackContext.jquery ) ?
                    jQuery( callbackContext ) :
                    jQuery.event,

                // Deferreds
                deferred = jQuery.Deferred(),
                completeDeferred = jQuery.Callbacks( "once memory" ),

                // Status-dependent callbacks
                statusCode = s.statusCode || {},

                // Headers (they are sent all at once)
                requestHeaders = {},
                requestHeadersNames = {},

                // Default abort message
                strAbort = "canceled",

                // Fake xhr
                jqXHR = {
                    readyState: 0,

                    // Builds headers hashtable if needed
                    getResponseHeader: function( key ) {
                        var match;
                        if ( completed ) {
                            if ( !responseHeaders ) {
                                responseHeaders = {};
                                while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
                                    responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
                                        ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
                                            .concat( match[ 2 ] );
                                }
                            }
                            match = responseHeaders[ key.toLowerCase() + " " ];
                        }
                        return match == null ? null : match.join( ", " );
                    },

                    // Raw string
                    getAllResponseHeaders: function() {
                        return completed ? responseHeadersString : null;
                    },

                    // Caches the header
                    setRequestHeader: function( name, value ) {
                        if ( completed == null ) {
                            name = requestHeadersNames[ name.toLowerCase() ] =
                                requestHeadersNames[ name.toLowerCase() ] || name;
                            requestHeaders[ name ] = value;
                        }
                        return this;
                    },

                    // Overrides response content-type header
                    overrideMimeType: function( type ) {
                        if ( completed == null ) {
                            s.mimeType = type;
                        }
                        return this;
                    },

                    // Status-dependent callbacks
                    statusCode: function( map ) {
                        var code;
                        if ( map ) {
                            if ( completed ) {

                                // Execute the appropriate callbacks
                                jqXHR.always( map[ jqXHR.status ] );
                            } else {

                                // Lazy-add the new callbacks in a way that preserves old ones
                                for ( code in map ) {
                                    statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
                                }
                            }
                        }
                        return this;
                    },

                    // Cancel the request
                    abort: function( statusText ) {
                        var finalText = statusText || strAbort;
                        if ( transport ) {
                            transport.abort( finalText );
                        }
                        done( 0, finalText );
                        return this;
                    }
                };

            // Attach deferreds
            deferred.promise( jqXHR );

            // Add protocol if not provided (prefilters might expect it)
            // Handle falsy url in the settings object (#10093: consistency with old signature)
            // We also use the url parameter if available
            s.url = ( ( url || s.url || location.href ) + "" )
                .replace( rprotocol, location.protocol + "//" );

            // Alias method option to type as per ticket #12004
            s.type = options.method || options.type || s.method || s.type;

            // Extract dataTypes list
            s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];

            // A cross-domain request is in order when the origin doesn't match the current origin.
            if ( s.crossDomain == null ) {
                urlAnchor = document.createElement( "a" );

                // Support: IE <=8 - 11, Edge 12 - 15
                // IE throws exception on accessing the href property if url is malformed,
                // e.g. http://example.com:80x/
                try {
                    urlAnchor.href = s.url;

                    // Support: IE <=8 - 11 only
                    // Anchor's host property isn't correctly set when s.url is relative
                    urlAnchor.href = urlAnchor.href;
                    s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
                        urlAnchor.protocol + "//" + urlAnchor.host;
                } catch ( e ) {

                    // If there is an error parsing the URL, assume it is crossDomain,
                    // it can be rejected by the transport if it is invalid
                    s.crossDomain = true;
                }
            }

            // Convert data if not already a string
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                s.data = jQuery.param( s.data, s.traditional );
            }

            // Apply prefilters
            inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );

            // If request was aborted inside a prefilter, stop there
            if ( completed ) {
                return jqXHR;
            }

            // We can fire global events as of now if asked to
            // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
            fireGlobals = jQuery.event && s.global;

            // Watch for a new set of requests
            if ( fireGlobals && jQuery.active++ === 0 ) {
                jQuery.event.trigger( "ajaxStart" );
            }

            // Uppercase the type
            s.type = s.type.toUpperCase();

            // Determine if request has content
            s.hasContent = !rnoContent.test( s.type );

            // Save the URL in case we're toying with the If-Modified-Since
            // and/or If-None-Match header later on
            // Remove hash to simplify url manipulation
            cacheURL = s.url.replace( rhash, "" );

            // More options handling for requests with no content
            if ( !s.hasContent ) {

                // Remember the hash so we can put it back
                uncached = s.url.slice( cacheURL.length );

                // If data is available and should be processed, append data to url
                if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
                    cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;

                    // #9682: remove data so that it's not used in an eventual retry
                    delete s.data;
                }

                // Add or update anti-cache param if needed
                if ( s.cache === false ) {
                    cacheURL = cacheURL.replace( rantiCache, "$1" );
                    uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) +
                        uncached;
                }

                // Put hash and anti-cache on the URL that will be requested (gh-1732)
                s.url = cacheURL + uncached;

                // Change '%20' to '+' if this is encoded form body content (gh-2658)
            } else if ( s.data && s.processData &&
                ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
                s.data = s.data.replace( r20, "+" );
            }

            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
            if ( s.ifModified ) {
                if ( jQuery.lastModified[ cacheURL ] ) {
                    jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
                }
                if ( jQuery.etag[ cacheURL ] ) {
                    jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
                }
            }

            // Set the correct header, if data is being sent
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                jqXHR.setRequestHeader( "Content-Type", s.contentType );
            }

            // Set the Accepts header for the server, depending on the dataType
            jqXHR.setRequestHeader(
                "Accept",
                s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
                    s.accepts[ s.dataTypes[ 0 ] ] +
                    ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
                    s.accepts[ "*" ]
            );

            // Check for headers option
            for ( i in s.headers ) {
                jqXHR.setRequestHeader( i, s.headers[ i ] );
            }

            // Allow custom headers/mimetypes and early abort
            if ( s.beforeSend &&
                ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {

                // Abort if not done already and return
                return jqXHR.abort();
            }

            // Aborting is no longer a cancellation
            strAbort = "abort";

            // Install callbacks on deferreds
            completeDeferred.add( s.complete );
            jqXHR.done( s.success );
            jqXHR.fail( s.error );

            // Get transport
            transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

            // If no transport, we auto-abort
            if ( !transport ) {
                done( -1, "No Transport" );
            } else {
                jqXHR.readyState = 1;

                // Send global event
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
                }

                // If request was aborted inside ajaxSend, stop there
                if ( completed ) {
                    return jqXHR;
                }

                // Timeout
                if ( s.async && s.timeout > 0 ) {
                    timeoutTimer = window.setTimeout( function() {
                        jqXHR.abort( "timeout" );
                    }, s.timeout );
                }

                try {
                    completed = false;
                    transport.send( requestHeaders, done );
                } catch ( e ) {

                    // Rethrow post-completion exceptions
                    if ( completed ) {
                        throw e;
                    }

                    // Propagate others as results
                    done( -1, e );
                }
            }

            // Callback for when everything is done
            function done( status, nativeStatusText, responses, headers ) {
                var isSuccess, success, error, response, modified,
                    statusText = nativeStatusText;

                // Ignore repeat invocations
                if ( completed ) {
                    return;
                }

                completed = true;

                // Clear timeout if it exists
                if ( timeoutTimer ) {
                    window.clearTimeout( timeoutTimer );
                }

                // Dereference transport for early garbage collection
                // (no matter how long the jqXHR object will be used)
                transport = undefined;

                // Cache response headers
                responseHeadersString = headers || "";

                // Set readyState
                jqXHR.readyState = status > 0 ? 4 : 0;

                // Determine if successful
                isSuccess = status >= 200 && status < 300 || status === 304;

                // Get response data
                if ( responses ) {
                    response = ajaxHandleResponses( s, jqXHR, responses );
                }

                // Use a noop converter for missing script but not if jsonp
                if ( !isSuccess &&
                    jQuery.inArray( "script", s.dataTypes ) > -1 &&
                    jQuery.inArray( "json", s.dataTypes ) < 0 ) {
                    s.converters[ "text script" ] = function() {};
                }

                // Convert no matter what (that way responseXXX fields are always set)
                response = ajaxConvert( s, response, jqXHR, isSuccess );

                // If successful, handle type chaining
                if ( isSuccess ) {

                    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
                    if ( s.ifModified ) {
                        modified = jqXHR.getResponseHeader( "Last-Modified" );
                        if ( modified ) {
                            jQuery.lastModified[ cacheURL ] = modified;
                        }
                        modified = jqXHR.getResponseHeader( "etag" );
                        if ( modified ) {
                            jQuery.etag[ cacheURL ] = modified;
                        }
                    }

                    // if no content
                    if ( status === 204 || s.type === "HEAD" ) {
                        statusText = "nocontent";

                        // if not modified
                    } else if ( status === 304 ) {
                        statusText = "notmodified";

                        // If we have data, let's convert it
                    } else {
                        statusText = response.state;
                        success = response.data;
                        error = response.error;
                        isSuccess = !error;
                    }
                } else {

                    // Extract error from statusText and normalize for non-aborts
                    error = statusText;
                    if ( status || !statusText ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                }

                // Set data for the fake xhr object
                jqXHR.status = status;
                jqXHR.statusText = ( nativeStatusText || statusText ) + "";

                // Success/Error
                if ( isSuccess ) {
                    deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
                }

                // Status-dependent callbacks
                jqXHR.statusCode( statusCode );
                statusCode = undefined;

                if ( fireGlobals ) {
                    globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
                        [ jqXHR, s, isSuccess ? success : error ] );
                }

                // Complete
                completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );

                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );

                    // Handle the global AJAX counter
                    if ( !( --jQuery.active ) ) {
                        jQuery.event.trigger( "ajaxStop" );
                    }
                }
            }

            return jqXHR;
        },

        getJSON: function( url, data, callback ) {
            return jQuery.get( url, data, callback, "json" );
        },

        getScript: function( url, callback ) {
            return jQuery.get( url, undefined, callback, "script" );
        }
    } );

    jQuery.each( [ "get", "post" ], function( _i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {

            // Shift arguments if data argument was omitted
            if ( isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = undefined;
            }

            // The url can be an options object (which then must have .url)
            return jQuery.ajax( jQuery.extend( {
                url: url,
                type: method,
                dataType: type,
                data: data,
                success: callback
            }, jQuery.isPlainObject( url ) && url ) );
        };
    } );

    jQuery.ajaxPrefilter( function( s ) {
        var i;
        for ( i in s.headers ) {
            if ( i.toLowerCase() === "content-type" ) {
                s.contentType = s.headers[ i ] || "";
            }
        }
    } );


    jQuery._evalUrl = function( url, options, doc ) {
        return jQuery.ajax( {
            url: url,

            // Make this explicit, since user can override this through ajaxSetup (#11264)
            type: "GET",
            dataType: "script",
            cache: true,
            async: false,
            global: false,

            // Only evaluate the response if it is successful (gh-4126)
            // dataFilter is not invoked for failure responses, so using it instead
            // of the default converter is kludgy but it works.
            converters: {
                "text script": function() {}
            },
            dataFilter: function( response ) {
                jQuery.globalEval( response, options, doc );
            }
        } );
    };


    jQuery.fn.extend( {
        wrapAll: function( html ) {
            var wrap;

            if ( this[ 0 ] ) {
                if ( isFunction( html ) ) {
                    html = html.call( this[ 0 ] );
                }

                // The elements to wrap the target around
                wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );

                if ( this[ 0 ].parentNode ) {
                    wrap.insertBefore( this[ 0 ] );
                }

                wrap.map( function() {
                    var elem = this;

                    while ( elem.firstElementChild ) {
                        elem = elem.firstElementChild;
                    }

                    return elem;
                } ).append( this );
            }

            return this;
        },

        wrapInner: function( html ) {
            if ( isFunction( html ) ) {
                return this.each( function( i ) {
                    jQuery( this ).wrapInner( html.call( this, i ) );
                } );
            }

            return this.each( function() {
                var self = jQuery( this ),
                    contents = self.contents();

                if ( contents.length ) {
                    contents.wrapAll( html );

                } else {
                    self.append( html );
                }
            } );
        },

        wrap: function( html ) {
            var htmlIsFunction = isFunction( html );

            return this.each( function( i ) {
                jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
            } );
        },

        unwrap: function( selector ) {
            this.parent( selector ).not( "body" ).each( function() {
                jQuery( this ).replaceWith( this.childNodes );
            } );
            return this;
        }
    } );


    jQuery.expr.pseudos.hidden = function( elem ) {
        return !jQuery.expr.pseudos.visible( elem );
    };
    jQuery.expr.pseudos.visible = function( elem ) {
        return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
    };




    jQuery.ajaxSettings.xhr = function() {
        try {
            return new window.XMLHttpRequest();
        } catch ( e ) {}
    };

    var xhrSuccessStatus = {

            // File protocol always yields status code 0, assume 200
            0: 200,

            // Support: IE <=9 only
            // #1450: sometimes IE returns 1223 when it should be 204
            1223: 204
        },
        xhrSupported = jQuery.ajaxSettings.xhr();

    support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
    support.ajax = xhrSupported = !!xhrSupported;

    jQuery.ajaxTransport( function( options ) {
        var callback, errorCallback;

        // Cross domain only allowed if supported through XMLHttpRequest
        if ( support.cors || xhrSupported && !options.crossDomain ) {
            return {
                send: function( headers, complete ) {
                    var i,
                        xhr = options.xhr();

                    xhr.open(
                        options.type,
                        options.url,
                        options.async,
                        options.username,
                        options.password
                    );

                    // Apply custom fields if provided
                    if ( options.xhrFields ) {
                        for ( i in options.xhrFields ) {
                            xhr[ i ] = options.xhrFields[ i ];
                        }
                    }

                    // Override mime type if needed
                    if ( options.mimeType && xhr.overrideMimeType ) {
                        xhr.overrideMimeType( options.mimeType );
                    }

                    // X-Requested-With header
                    // For cross-domain requests, seeing as conditions for a preflight are
                    // akin to a jigsaw puzzle, we simply never set it to be sure.
                    // (it can always be set on a per-request basis or even using ajaxSetup)
                    // For same-domain requests, won't change header if already provided.
                    if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
                        headers[ "X-Requested-With" ] = "XMLHttpRequest";
                    }

                    // Set headers
                    for ( i in headers ) {
                        xhr.setRequestHeader( i, headers[ i ] );
                    }

                    // Callback
                    callback = function( type ) {
                        return function() {
                            if ( callback ) {
                                callback = errorCallback = xhr.onload =
                                    xhr.onerror = xhr.onabort = xhr.ontimeout =
                                        xhr.onreadystatechange = null;

                                if ( type === "abort" ) {
                                    xhr.abort();
                                } else if ( type === "error" ) {

                                    // Support: IE <=9 only
                                    // On a manual native abort, IE9 throws
                                    // errors on any property access that is not readyState
                                    if ( typeof xhr.status !== "number" ) {
                                        complete( 0, "error" );
                                    } else {
                                        complete(

                                            // File: protocol always yields status 0; see #8605, #14207
                                            xhr.status,
                                            xhr.statusText
                                        );
                                    }
                                } else {
                                    complete(
                                        xhrSuccessStatus[ xhr.status ] || xhr.status,
                                        xhr.statusText,

                                        // Support: IE <=9 only
                                        // IE9 has no XHR2 but throws on binary (trac-11426)
                                        // For XHR2 non-text, let the caller handle it (gh-2498)
                                        ( xhr.responseType || "text" ) !== "text"  ||
                                        typeof xhr.responseText !== "string" ?
                                            { binary: xhr.response } :
                                            { text: xhr.responseText },
                                        xhr.getAllResponseHeaders()
                                    );
                                }
                            }
                        };
                    };

                    // Listen to events
                    xhr.onload = callback();
                    errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );

                    // Support: IE 9 only
                    // Use onreadystatechange to replace onabort
                    // to handle uncaught aborts
                    if ( xhr.onabort !== undefined ) {
                        xhr.onabort = errorCallback;
                    } else {
                        xhr.onreadystatechange = function() {

                            // Check readyState before timeout as it changes
                            if ( xhr.readyState === 4 ) {

                                // Allow onerror to be called first,
                                // but that will not handle a native abort
                                // Also, save errorCallback to a variable
                                // as xhr.onerror cannot be accessed
                                window.setTimeout( function() {
                                    if ( callback ) {
                                        errorCallback();
                                    }
                                } );
                            }
                        };
                    }

                    // Create the abort callback
                    callback = callback( "abort" );

                    try {

                        // Do send the request (this may raise an exception)
                        xhr.send( options.hasContent && options.data || null );
                    } catch ( e ) {

                        // #14683: Only rethrow if this hasn't been notified as an error yet
                        if ( callback ) {
                            throw e;
                        }
                    }
                },

                abort: function() {
                    if ( callback ) {
                        callback();
                    }
                }
            };
        }
    } );




// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
    jQuery.ajaxPrefilter( function( s ) {
        if ( s.crossDomain ) {
            s.contents.script = false;
        }
    } );

// Install script dataType
    jQuery.ajaxSetup( {
        accepts: {
            script: "text/javascript, application/javascript, " +
                "application/ecmascript, application/x-ecmascript"
        },
        contents: {
            script: /\b(?:java|ecma)script\b/
        },
        converters: {
            "text script": function( text ) {
                jQuery.globalEval( text );
                return text;
            }
        }
    } );

// Handle cache's special case and crossDomain
    jQuery.ajaxPrefilter( "script", function( s ) {
        if ( s.cache === undefined ) {
            s.cache = false;
        }
        if ( s.crossDomain ) {
            s.type = "GET";
        }
    } );

// Bind script tag hack transport
    jQuery.ajaxTransport( "script", function( s ) {

        // This transport only deals with cross domain or forced-by-attrs requests
        if ( s.crossDomain || s.scriptAttrs ) {
            var script, callback;
            return {
                send: function( _, complete ) {
                    script = jQuery( "<script>" )
                        .attr( s.scriptAttrs || {} )
                        .prop( { charset: s.scriptCharset, src: s.url } )
                        .on( "load error", callback = function( evt ) {
                            script.remove();
                            callback = null;
                            if ( evt ) {
                                complete( evt.type === "error" ? 404 : 200, evt.type );
                            }
                        } );

                    // Use native DOM manipulation to avoid our domManip AJAX trickery
                    document.head.appendChild( script[ 0 ] );
                },
                abort: function() {
                    if ( callback ) {
                        callback();
                    }
                }
            };
        }
    } );




    var oldCallbacks = [],
        rjsonp = /(=)\?(?=&|$)|\?\?/;

// Default jsonp settings
    jQuery.ajaxSetup( {
        jsonp: "callback",
        jsonpCallback: function() {
            var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) );
            this[ callback ] = true;
            return callback;
        }
    } );

// Detect, normalize options and install callbacks for jsonp requests
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {

        var callbackName, overwritten, responseContainer,
            jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
                    "url" :
                    typeof s.data === "string" &&
                    ( s.contentType || "" )
                        .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
                    rjsonp.test( s.data ) && "data"
            );

        // Handle iff the expected data type is "jsonp" or we have a parameter to set
        if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {

            // Get callback name, remembering preexisting value associated with it
            callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
                s.jsonpCallback() :
                s.jsonpCallback;

            // Insert callback into url or form data
            if ( jsonProp ) {
                s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
            } else if ( s.jsonp !== false ) {
                s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
            }

            // Use data converter to retrieve json after script execution
            s.converters[ "script json" ] = function() {
                if ( !responseContainer ) {
                    jQuery.error( callbackName + " was not called" );
                }
                return responseContainer[ 0 ];
            };

            // Force json dataType
            s.dataTypes[ 0 ] = "json";

            // Install callback
            overwritten = window[ callbackName ];
            window[ callbackName ] = function() {
                responseContainer = arguments;
            };

            // Clean-up function (fires after converters)
            jqXHR.always( function() {

                // If previous value didn't exist - remove it
                if ( overwritten === undefined ) {
                    jQuery( window ).removeProp( callbackName );

                    // Otherwise restore preexisting value
                } else {
                    window[ callbackName ] = overwritten;
                }

                // Save back as free
                if ( s[ callbackName ] ) {

                    // Make sure that re-using the options doesn't screw things around
                    s.jsonpCallback = originalSettings.jsonpCallback;

                    // Save the callback name for future use
                    oldCallbacks.push( callbackName );
                }

                // Call if it was a function and we have a response
                if ( responseContainer && isFunction( overwritten ) ) {
                    overwritten( responseContainer[ 0 ] );
                }

                responseContainer = overwritten = undefined;
            } );

            // Delegate to script
            return "script";
        }
    } );




// Support: Safari 8 only
// In Safari 8 documents created via document.implementation.createHTMLDocument
// collapse sibling forms: the second one becomes a child of the first one.
// Because of that, this security measure has to be disabled in Safari 8.
// https://bugs.webkit.org/show_bug.cgi?id=137337
    support.createHTMLDocument = ( function() {
        var body = document.implementation.createHTMLDocument( "" ).body;
        body.innerHTML = "<form></form><form></form>";
        return body.childNodes.length === 2;
    } )();


// Argument "data" should be string of html
// context (optional): If specified, the fragment will be created in this context,
// defaults to document
// keepScripts (optional): If true, will include scripts passed in the html string
    jQuery.parseHTML = function( data, context, keepScripts ) {
        if ( typeof data !== "string" ) {
            return [];
        }
        if ( typeof context === "boolean" ) {
            keepScripts = context;
            context = false;
        }

        var base, parsed, scripts;

        if ( !context ) {

            // Stop scripts or inline event handlers from being executed immediately
            // by using document.implementation
            if ( support.createHTMLDocument ) {
                context = document.implementation.createHTMLDocument( "" );

                // Set the base href for the created document
                // so any parsed elements with URLs
                // are based on the document's URL (gh-2965)
                base = context.createElement( "base" );
                base.href = document.location.href;
                context.head.appendChild( base );
            } else {
                context = document;
            }
        }

        parsed = rsingleTag.exec( data );
        scripts = !keepScripts && [];

        // Single tag
        if ( parsed ) {
            return [ context.createElement( parsed[ 1 ] ) ];
        }

        parsed = buildFragment( [ data ], context, scripts );

        if ( scripts && scripts.length ) {
            jQuery( scripts ).remove();
        }

        return jQuery.merge( [], parsed.childNodes );
    };


    /**
     * Load a url into a page
     */
    jQuery.fn.load = function( url, params, callback ) {
        var selector, type, response,
            self = this,
            off = url.indexOf( " " );

        if ( off > -1 ) {
            selector = stripAndCollapse( url.slice( off ) );
            url = url.slice( 0, off );
        }

        // If it's a function
        if ( isFunction( params ) ) {

            // We assume that it's the callback
            callback = params;
            params = undefined;

            // Otherwise, build a param string
        } else if ( params && typeof params === "object" ) {
            type = "POST";
        }

        // If we have elements to modify, make the request
        if ( self.length > 0 ) {
            jQuery.ajax( {
                url: url,

                // If "type" variable is undefined, then "GET" method will be used.
                // Make value of this field explicit since
                // user can override it through ajaxSetup method
                type: type || "GET",
                dataType: "html",
                data: params
            } ).done( function( responseText ) {

                // Save response for use in complete callback
                response = arguments;

                self.html( selector ?

                    // If a selector was specified, locate the right elements in a dummy div
                    // Exclude scripts to avoid IE 'Permission Denied' errors
                    jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :

                    // Otherwise use the full result
                    responseText );

                // If the request succeeds, this function gets "data", "status", "jqXHR"
                // but they are ignored because response was set above.
                // If it fails, this function gets "jqXHR", "status", "error"
            } ).always( callback && function( jqXHR, status ) {
                self.each( function() {
                    callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
                } );
            } );
        }

        return this;
    };




    jQuery.expr.pseudos.animated = function( elem ) {
        return jQuery.grep( jQuery.timers, function( fn ) {
            return elem === fn.elem;
        } ).length;
    };




    jQuery.offset = {
        setOffset: function( elem, options, i ) {
            var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
                position = jQuery.css( elem, "position" ),
                curElem = jQuery( elem ),
                props = {};

            // Set position first, in-case top/left are set even on static elem
            if ( position === "static" ) {
                elem.style.position = "relative";
            }

            curOffset = curElem.offset();
            curCSSTop = jQuery.css( elem, "top" );
            curCSSLeft = jQuery.css( elem, "left" );
            calculatePosition = ( position === "absolute" || position === "fixed" ) &&
                ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;

            // Need to be able to calculate position if either
            // top or left is auto and position is either absolute or fixed
            if ( calculatePosition ) {
                curPosition = curElem.position();
                curTop = curPosition.top;
                curLeft = curPosition.left;

            } else {
                curTop = parseFloat( curCSSTop ) || 0;
                curLeft = parseFloat( curCSSLeft ) || 0;
            }

            if ( isFunction( options ) ) {

                // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
                options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
            }

            if ( options.top != null ) {
                props.top = ( options.top - curOffset.top ) + curTop;
            }
            if ( options.left != null ) {
                props.left = ( options.left - curOffset.left ) + curLeft;
            }

            if ( "using" in options ) {
                options.using.call( elem, props );

            } else {
                curElem.css( props );
            }
        }
    };

    jQuery.fn.extend( {

        // offset() relates an element's border box to the document origin
        offset: function( options ) {

            // Preserve chaining for setter
            if ( arguments.length ) {
                return options === undefined ?
                    this :
                    this.each( function( i ) {
                        jQuery.offset.setOffset( this, options, i );
                    } );
            }

            var rect, win,
                elem = this[ 0 ];

            if ( !elem ) {
                return;
            }

            // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
            // Support: IE <=11 only
            // Running getBoundingClientRect on a
            // disconnected node in IE throws an error
            if ( !elem.getClientRects().length ) {
                return { top: 0, left: 0 };
            }

            // Get document-relative position by adding viewport scroll to viewport-relative gBCR
            rect = elem.getBoundingClientRect();
            win = elem.ownerDocument.defaultView;
            return {
                top: rect.top + win.pageYOffset,
                left: rect.left + win.pageXOffset
            };
        },

        // position() relates an element's margin box to its offset parent's padding box
        // This corresponds to the behavior of CSS absolute positioning
        position: function() {
            if ( !this[ 0 ] ) {
                return;
            }

            var offsetParent, offset, doc,
                elem = this[ 0 ],
                parentOffset = { top: 0, left: 0 };

            // position:fixed elements are offset from the viewport, which itself always has zero offset
            if ( jQuery.css( elem, "position" ) === "fixed" ) {

                // Assume position:fixed implies availability of getBoundingClientRect
                offset = elem.getBoundingClientRect();

            } else {
                offset = this.offset();

                // Account for the *real* offset parent, which can be the document or its root element
                // when a statically positioned element is identified
                doc = elem.ownerDocument;
                offsetParent = elem.offsetParent || doc.documentElement;
                while ( offsetParent &&
                ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
                jQuery.css( offsetParent, "position" ) === "static" ) {

                    offsetParent = offsetParent.parentNode;
                }
                if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {

                    // Incorporate borders into its offset, since they are outside its content origin
                    parentOffset = jQuery( offsetParent ).offset();
                    parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
                    parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
                }
            }

            // Subtract parent offsets and element margins
            return {
                top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
                left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
            };
        },

        // This method will return documentElement in the following cases:
        // 1) For the element inside the iframe without offsetParent, this method will return
        //    documentElement of the parent window
        // 2) For the hidden or detached element
        // 3) For body or html element, i.e. in case of the html node - it will return itself
        //
        // but those exceptions were never presented as a real life use-cases
        // and might be considered as more preferable results.
        //
        // This logic, however, is not guaranteed and can change at any point in the future
        offsetParent: function() {
            return this.map( function() {
                var offsetParent = this.offsetParent;

                while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
                    offsetParent = offsetParent.offsetParent;
                }

                return offsetParent || documentElement;
            } );
        }
    } );

// Create scrollLeft and scrollTop methods
    jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
        var top = "pageYOffset" === prop;

        jQuery.fn[ method ] = function( val ) {
            return access( this, function( elem, method, val ) {

                // Coalesce documents and windows
                var win;
                if ( isWindow( elem ) ) {
                    win = elem;
                } else if ( elem.nodeType === 9 ) {
                    win = elem.defaultView;
                }

                if ( val === undefined ) {
                    return win ? win[ prop ] : elem[ method ];
                }

                if ( win ) {
                    win.scrollTo(
                        !top ? val : win.pageXOffset,
                        top ? val : win.pageYOffset
                    );

                } else {
                    elem[ method ] = val;
                }
            }, method, val, arguments.length );
        };
    } );

// Support: Safari <=7 - 9.1, Chrome <=37 - 49
// Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
// getComputedStyle returns percent when specified for top/left/bottom/right;
// rather than make the css module depend on the offset module, just check for it here
    jQuery.each( [ "top", "left" ], function( _i, prop ) {
        jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
            function( elem, computed ) {
                if ( computed ) {
                    computed = curCSS( elem, prop );

                    // If curCSS returns percentage, fallback to offset
                    return rnumnonpx.test( computed ) ?
                        jQuery( elem ).position()[ prop ] + "px" :
                        computed;
                }
            }
        );
    } );


// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
    jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
        jQuery.each( {
            padding: "inner" + name,
            content: type,
            "": "outer" + name
        }, function( defaultExtra, funcName ) {

            // Margin is only for outerHeight, outerWidth
            jQuery.fn[ funcName ] = function( margin, value ) {
                var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
                    extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );

                return access( this, function( elem, type, value ) {
                    var doc;

                    if ( isWindow( elem ) ) {

                        // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
                        return funcName.indexOf( "outer" ) === 0 ?
                            elem[ "inner" + name ] :
                            elem.document.documentElement[ "client" + name ];
                    }

                    // Get document width or height
                    if ( elem.nodeType === 9 ) {
                        doc = elem.documentElement;

                        // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
                        // whichever is greatest
                        return Math.max(
                            elem.body[ "scroll" + name ], doc[ "scroll" + name ],
                            elem.body[ "offset" + name ], doc[ "offset" + name ],
                            doc[ "client" + name ]
                        );
                    }

                    return value === undefined ?

                        // Get width or height on the element, requesting but not forcing parseFloat
                        jQuery.css( elem, type, extra ) :

                        // Set width or height on the element
                        jQuery.style( elem, type, value, extra );
                }, type, chainable ? margin : undefined, chainable );
            };
        } );
    } );


    jQuery.each( [
        "ajaxStart",
        "ajaxStop",
        "ajaxComplete",
        "ajaxError",
        "ajaxSuccess",
        "ajaxSend"
    ], function( _i, type ) {
        jQuery.fn[ type ] = function( fn ) {
            return this.on( type, fn );
        };
    } );




    jQuery.fn.extend( {

        bind: function( types, data, fn ) {
            return this.on( types, null, data, fn );
        },
        unbind: function( types, fn ) {
            return this.off( types, null, fn );
        },

        delegate: function( selector, types, data, fn ) {
            return this.on( types, selector, data, fn );
        },
        undelegate: function( selector, types, fn ) {

            // ( namespace ) or ( selector, types [, fn] )
            return arguments.length === 1 ?
                this.off( selector, "**" ) :
                this.off( types, selector || "**", fn );
        },

        hover: function( fnOver, fnOut ) {
            return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
        }
    } );

    jQuery.each(
        ( "blur focus focusin focusout resize scroll click dblclick " +
            "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
            "change select submit keydown keypress keyup contextmenu" ).split( " " ),
        function( _i, name ) {

            // Handle event binding
            jQuery.fn[ name ] = function( data, fn ) {
                return arguments.length > 0 ?
                    this.on( name, null, data, fn ) :
                    this.trigger( name );
            };
        }
    );




// Support: Android <=4.0 only
// Make sure we trim BOM and NBSP
    var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

// Bind a function to a context, optionally partially applying any
// arguments.
// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
// However, it is not slated for removal any time soon
    jQuery.proxy = function( fn, context ) {
        var tmp, args, proxy;

        if ( typeof context === "string" ) {
            tmp = fn[ context ];
            context = fn;
            fn = tmp;
        }

        // Quick check to determine if target is callable, in the spec
        // this throws a TypeError, but we will just return undefined.
        if ( !isFunction( fn ) ) {
            return undefined;
        }

        // Simulated bind
        args = slice.call( arguments, 2 );
        proxy = function() {
            return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
        };

        // Set the guid of unique handler to the same of original handler, so it can be removed
        proxy.guid = fn.guid = fn.guid || jQuery.guid++;

        return proxy;
    };

    jQuery.holdReady = function( hold ) {
        if ( hold ) {
            jQuery.readyWait++;
        } else {
            jQuery.ready( true );
        }
    };
    jQuery.isArray = Array.isArray;
    jQuery.parseJSON = JSON.parse;
    jQuery.nodeName = nodeName;
    jQuery.isFunction = isFunction;
    jQuery.isWindow = isWindow;
    jQuery.camelCase = camelCase;
    jQuery.type = toType;

    jQuery.now = Date.now;

    jQuery.isNumeric = function( obj ) {

        // As of jQuery 3.0, isNumeric is limited to
        // strings and numbers (primitives or objects)
        // that can be coerced to finite numbers (gh-2662)
        var type = jQuery.type( obj );
        return ( type === "number" || type === "string" ) &&

            // parseFloat NaNs numeric-cast false positives ("")
            // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
            // subtraction forces infinities to NaN
            !isNaN( obj - parseFloat( obj ) );
    };

    jQuery.trim = function( text ) {
        return text == null ?
            "" :
            ( text + "" ).replace( rtrim, "" );
    };



// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

    if ( typeof define === "function" && define.amd ) {
        define( "jquery", [], function() {
            return jQuery;
        } );
    }




    var

        // Map over jQuery in case of overwrite
        _jQuery = window.jQuery,

        // Map over the $ in case of overwrite
        _$ = window.$;

    jQuery.noConflict = function( deep ) {
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }

        return jQuery;
    };

// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
    if ( typeof noGlobal === "undefined" ) {
        window.jQuery = window.$ = jQuery;
    }




    return jQuery;
} );

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define('underscore', factory) :
            (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (function () {
                var current = global._;
                var exports = global._ = factory();
                exports.noConflict = function () { global._ = current; return exports; };
            }()));
}(this, (function () {
    //     Underscore.js 1.13.2
    //     https://underscorejs.org
    //     (c) 2009-2021 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors
    //     Underscore may be freely distributed under the MIT license.

    // Current version.
    var VERSION = '1.13.2';

    // Establish the root object, `window` (`self`) in the browser, `global`
    // on the server, or `this` in some virtual machines. We use `self`
    // instead of `window` for `WebWorker` support.
    var root = typeof self == 'object' && self.self === self && self ||
        typeof global == 'object' && global.global === global && global ||
        Function('return this')() ||
        {};

    // Save bytes in the minified (but not gzipped) version:
    var ArrayProto = Array.prototype, ObjProto = Object.prototype;
    var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;

    // Create quick reference variables for speed access to core prototypes.
    var push = ArrayProto.push,
        slice = ArrayProto.slice,
        toString = ObjProto.toString,
        hasOwnProperty = ObjProto.hasOwnProperty;

    // Modern feature detection.
    var supportsArrayBuffer = typeof ArrayBuffer !== 'undefined',
        supportsDataView = typeof DataView !== 'undefined';

    // All **ECMAScript 5+** native function implementations that we hope to use
    // are declared here.
    var nativeIsArray = Array.isArray,
        nativeKeys = Object.keys,
        nativeCreate = Object.create,
        nativeIsView = supportsArrayBuffer && ArrayBuffer.isView;

    // Create references to these builtin functions because we override them.
    var _isNaN = isNaN,
        _isFinite = isFinite;

    // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
    var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
    var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
        'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];

    // The largest integer that can be represented exactly.
    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;

    // Some functions take a variable number of arguments, or a few expected
    // arguments at the beginning and then a variable number of values to operate
    // on. This helper accumulates all remaining arguments past the function’s
    // argument length (or an explicit `startIndex`), into an array that becomes
    // the last argument. Similar to ES6’s "rest parameter".
    function restArguments(func, startIndex) {
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        return function() {
            var length = Math.max(arguments.length - startIndex, 0),
                rest = Array(length),
                index = 0;
            for (; index < length; index++) {
                rest[index] = arguments[index + startIndex];
            }
            switch (startIndex) {
                case 0: return func.call(this, rest);
                case 1: return func.call(this, arguments[0], rest);
                case 2: return func.call(this, arguments[0], arguments[1], rest);
            }
            var args = Array(startIndex + 1);
            for (index = 0; index < startIndex; index++) {
                args[index] = arguments[index];
            }
            args[startIndex] = rest;
            return func.apply(this, args);
        };
    }

    // Is a given variable an object?
    function isObject(obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;
    }

    // Is a given value equal to null?
    function isNull(obj) {
        return obj === null;
    }

    // Is a given variable undefined?
    function isUndefined(obj) {
        return obj === void 0;
    }

    // Is a given value a boolean?
    function isBoolean(obj) {
        return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
    }

    // Is a given value a DOM element?
    function isElement(obj) {
        return !!(obj && obj.nodeType === 1);
    }

    // Internal function for creating a `toString`-based type tester.
    function tagTester(name) {
        var tag = '[object ' + name + ']';
        return function(obj) {
            return toString.call(obj) === tag;
        };
    }

    var isString = tagTester('String');

    var isNumber = tagTester('Number');

    var isDate = tagTester('Date');

    var isRegExp = tagTester('RegExp');

    var isError = tagTester('Error');

    var isSymbol = tagTester('Symbol');

    var isArrayBuffer = tagTester('ArrayBuffer');

    var isFunction = tagTester('Function');

    // Optimize `isFunction` if appropriate. Work around some `typeof` bugs in old
    // v8, IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
    var nodelist = root.document && root.document.childNodes;
    if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
        isFunction = function(obj) {
            return typeof obj == 'function' || false;
        };
    }

    var isFunction$1 = isFunction;

    var hasObjectTag = tagTester('Object');

    // In IE 10 - Edge 13, `DataView` has string tag `'[object Object]'`.
    // In IE 11, the most common among them, this problem also applies to
    // `Map`, `WeakMap` and `Set`.
    var hasStringTagBug = (
            supportsDataView && hasObjectTag(new DataView(new ArrayBuffer(8)))
        ),
        isIE11 = (typeof Map !== 'undefined' && hasObjectTag(new Map));

    var isDataView = tagTester('DataView');

    // In IE 10 - Edge 13, we need a different heuristic
    // to determine whether an object is a `DataView`.
    function ie10IsDataView(obj) {
        return obj != null && isFunction$1(obj.getInt8) && isArrayBuffer(obj.buffer);
    }

    var isDataView$1 = (hasStringTagBug ? ie10IsDataView : isDataView);

    // Is a given value an array?
    // Delegates to ECMA5's native `Array.isArray`.
    var isArray = nativeIsArray || tagTester('Array');

    // Internal function to check whether `key` is an own property name of `obj`.
    function has$1(obj, key) {
        return obj != null && hasOwnProperty.call(obj, key);
    }

    var isArguments = tagTester('Arguments');

    // Define a fallback version of the method in browsers (ahem, IE < 9), where
    // there isn't any inspectable "Arguments" type.
    (function() {
        if (!isArguments(arguments)) {
            isArguments = function(obj) {
                return has$1(obj, 'callee');
            };
        }
    }());

    var isArguments$1 = isArguments;

    // Is a given object a finite number?
    function isFinite$1(obj) {
        return !isSymbol(obj) && _isFinite(obj) && !isNaN(parseFloat(obj));
    }

    // Is the given value `NaN`?
    function isNaN$1(obj) {
        return isNumber(obj) && _isNaN(obj);
    }

    // Predicate-generating function. Often useful outside of Underscore.
    function constant(value) {
        return function() {
            return value;
        };
    }

    // Common internal logic for `isArrayLike` and `isBufferLike`.
    function createSizePropertyCheck(getSizeProperty) {
        return function(collection) {
            var sizeProperty = getSizeProperty(collection);
            return typeof sizeProperty == 'number' && sizeProperty >= 0 && sizeProperty <= MAX_ARRAY_INDEX;
        }
    }

    // Internal helper to generate a function to obtain property `key` from `obj`.
    function shallowProperty(key) {
        return function(obj) {
            return obj == null ? void 0 : obj[key];
        };
    }

    // Internal helper to obtain the `byteLength` property of an object.
    var getByteLength = shallowProperty('byteLength');

    // Internal helper to determine whether we should spend extensive checks against
    // `ArrayBuffer` et al.
    var isBufferLike = createSizePropertyCheck(getByteLength);

    // Is a given value a typed array?
    var typedArrayPattern = /\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;
    function isTypedArray(obj) {
        // `ArrayBuffer.isView` is the most future-proof, so use it when available.
        // Otherwise, fall back on the above regular expression.
        return nativeIsView ? (nativeIsView(obj) && !isDataView$1(obj)) :
            isBufferLike(obj) && typedArrayPattern.test(toString.call(obj));
    }

    var isTypedArray$1 = supportsArrayBuffer ? isTypedArray : constant(false);

    // Internal helper to obtain the `length` property of an object.
    var getLength = shallowProperty('length');

    // Internal helper to create a simple lookup structure.
    // `collectNonEnumProps` used to depend on `_.contains`, but this led to
    // circular imports. `emulatedSet` is a one-off solution that only works for
    // arrays of strings.
    function emulatedSet(keys) {
        var hash = {};
        for (var l = keys.length, i = 0; i < l; ++i) hash[keys[i]] = true;
        return {
            contains: function(key) { return hash[key] === true; },
            push: function(key) {
                hash[key] = true;
                return keys.push(key);
            }
        };
    }

    // Internal helper. Checks `keys` for the presence of keys in IE < 9 that won't
    // be iterated by `for key in ...` and thus missed. Extends `keys` in place if
    // needed.
    function collectNonEnumProps(obj, keys) {
        keys = emulatedSet(keys);
        var nonEnumIdx = nonEnumerableProps.length;
        var constructor = obj.constructor;
        var proto = isFunction$1(constructor) && constructor.prototype || ObjProto;

        // Constructor is a special case.
        var prop = 'constructor';
        if (has$1(obj, prop) && !keys.contains(prop)) keys.push(prop);

        while (nonEnumIdx--) {
            prop = nonEnumerableProps[nonEnumIdx];
            if (prop in obj && obj[prop] !== proto[prop] && !keys.contains(prop)) {
                keys.push(prop);
            }
        }
    }

    // Retrieve the names of an object's own properties.
    // Delegates to **ECMAScript 5**'s native `Object.keys`.
    function keys(obj) {
        if (!isObject(obj)) return [];
        if (nativeKeys) return nativeKeys(obj);
        var keys = [];
        for (var key in obj) if (has$1(obj, key)) keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);
        return keys;
    }

    // Is a given array, string, or object empty?
    // An "empty" object has no enumerable own-properties.
    function isEmpty(obj) {
        if (obj == null) return true;
        // Skip the more expensive `toString`-based type checks if `obj` has no
        // `.length`.
        var length = getLength(obj);
        if (typeof length == 'number' && (
            isArray(obj) || isString(obj) || isArguments$1(obj)
        )) return length === 0;
        return getLength(keys(obj)) === 0;
    }

    // Returns whether an object has a given set of `key:value` pairs.
    function isMatch(object, attrs) {
        var _keys = keys(attrs), length = _keys.length;
        if (object == null) return !length;
        var obj = Object(object);
        for (var i = 0; i < length; i++) {
            var key = _keys[i];
            if (attrs[key] !== obj[key] || !(key in obj)) return false;
        }
        return true;
    }

    // If Underscore is called as a function, it returns a wrapped object that can
    // be used OO-style. This wrapper holds altered versions of all functions added
    // through `_.mixin`. Wrapped objects may be chained.
    function _$1(obj) {
        if (obj instanceof _$1) return obj;
        if (!(this instanceof _$1)) return new _$1(obj);
        this._wrapped = obj;
    }

    _$1.VERSION = VERSION;

    // Extracts the result from a wrapped and chained object.
    _$1.prototype.value = function() {
        return this._wrapped;
    };

    // Provide unwrapping proxies for some methods used in engine operations
    // such as arithmetic and JSON stringification.
    _$1.prototype.valueOf = _$1.prototype.toJSON = _$1.prototype.value;

    _$1.prototype.toString = function() {
        return String(this._wrapped);
    };

    // Internal function to wrap or shallow-copy an ArrayBuffer,
    // typed array or DataView to a new view, reusing the buffer.
    function toBufferView(bufferSource) {
        return new Uint8Array(
            bufferSource.buffer || bufferSource,
            bufferSource.byteOffset || 0,
            getByteLength(bufferSource)
        );
    }

    // We use this string twice, so give it a name for minification.
    var tagDataView = '[object DataView]';

    // Internal recursive comparison function for `_.isEqual`.
    function eq(a, b, aStack, bStack) {
        // Identical objects are equal. `0 === -0`, but they aren't identical.
        // See the [Harmony `egal` proposal](https://wiki.ecmascript.org/doku.php?id=harmony:egal).
        if (a === b) return a !== 0 || 1 / a === 1 / b;
        // `null` or `undefined` only equal to itself (strict comparison).
        if (a == null || b == null) return false;
        // `NaN`s are equivalent, but non-reflexive.
        if (a !== a) return b !== b;
        // Exhaust primitive checks
        var type = typeof a;
        if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
        return deepEq(a, b, aStack, bStack);
    }

    // Internal recursive comparison function for `_.isEqual`.
    function deepEq(a, b, aStack, bStack) {
        // Unwrap any wrapped objects.
        if (a instanceof _$1) a = a._wrapped;
        if (b instanceof _$1) b = b._wrapped;
        // Compare `[[Class]]` names.
        var className = toString.call(a);
        if (className !== toString.call(b)) return false;
        // Work around a bug in IE 10 - Edge 13.
        if (hasStringTagBug && className == '[object Object]' && isDataView$1(a)) {
            if (!isDataView$1(b)) return false;
            className = tagDataView;
        }
        switch (className) {
            // These types are compared by value.
            case '[object RegExp]':
            // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
            case '[object String]':
                // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
                // equivalent to `new String("5")`.
                return '' + a === '' + b;
            case '[object Number]':
                // `NaN`s are equivalent, but non-reflexive.
                // Object(NaN) is equivalent to NaN.
                if (+a !== +a) return +b !== +b;
                // An `egal` comparison is performed for other numeric values.
                return +a === 0 ? 1 / +a === 1 / b : +a === +b;
            case '[object Date]':
            case '[object Boolean]':
                // Coerce dates and booleans to numeric primitive values. Dates are compared by their
                // millisecond representations. Note that invalid dates with millisecond representations
                // of `NaN` are not equivalent.
                return +a === +b;
            case '[object Symbol]':
                return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
            case '[object ArrayBuffer]':
            case tagDataView:
                // Coerce to typed array so we can fall through.
                return deepEq(toBufferView(a), toBufferView(b), aStack, bStack);
        }

        var areArrays = className === '[object Array]';
        if (!areArrays && isTypedArray$1(a)) {
            var byteLength = getByteLength(a);
            if (byteLength !== getByteLength(b)) return false;
            if (a.buffer === b.buffer && a.byteOffset === b.byteOffset) return true;
            areArrays = true;
        }
        if (!areArrays) {
            if (typeof a != 'object' || typeof b != 'object') return false;

            // Objects with different constructors are not equivalent, but `Object`s or `Array`s
            // from different frames are.
            var aCtor = a.constructor, bCtor = b.constructor;
            if (aCtor !== bCtor && !(isFunction$1(aCtor) && aCtor instanceof aCtor &&
                isFunction$1(bCtor) && bCtor instanceof bCtor)
                && ('constructor' in a && 'constructor' in b)) {
                return false;
            }
        }
        // Assume equality for cyclic structures. The algorithm for detecting cyclic
        // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.

        // Initializing stack of traversed objects.
        // It's done here since we only need them for objects and arrays comparison.
        aStack = aStack || [];
        bStack = bStack || [];
        var length = aStack.length;
        while (length--) {
            // Linear search. Performance is inversely proportional to the number of
            // unique nested structures.
            if (aStack[length] === a) return bStack[length] === b;
        }

        // Add the first object to the stack of traversed objects.
        aStack.push(a);
        bStack.push(b);

        // Recursively compare objects and arrays.
        if (areArrays) {
            // Compare array lengths to determine if a deep comparison is necessary.
            length = a.length;
            if (length !== b.length) return false;
            // Deep compare the contents, ignoring non-numeric properties.
            while (length--) {
                if (!eq(a[length], b[length], aStack, bStack)) return false;
            }
        } else {
            // Deep compare objects.
            var _keys = keys(a), key;
            length = _keys.length;
            // Ensure that both objects contain the same number of properties before comparing deep equality.
            if (keys(b).length !== length) return false;
            while (length--) {
                // Deep compare each member
                key = _keys[length];
                if (!(has$1(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
            }
        }
        // Remove the first object from the stack of traversed objects.
        aStack.pop();
        bStack.pop();
        return true;
    }

    // Perform a deep comparison to check if two objects are equal.
    function isEqual(a, b) {
        return eq(a, b);
    }

    // Retrieve all the enumerable property names of an object.
    function allKeys(obj) {
        if (!isObject(obj)) return [];
        var keys = [];
        for (var key in obj) keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);
        return keys;
    }

    // Since the regular `Object.prototype.toString` type tests don't work for
    // some types in IE 11, we use a fingerprinting heuristic instead, based
    // on the methods. It's not great, but it's the best we got.
    // The fingerprint method lists are defined below.
    function ie11fingerprint(methods) {
        var length = getLength(methods);
        return function(obj) {
            if (obj == null) return false;
            // `Map`, `WeakMap` and `Set` have no enumerable keys.
            var keys = allKeys(obj);
            if (getLength(keys)) return false;
            for (var i = 0; i < length; i++) {
                if (!isFunction$1(obj[methods[i]])) return false;
            }
            // If we are testing against `WeakMap`, we need to ensure that
            // `obj` doesn't have a `forEach` method in order to distinguish
            // it from a regular `Map`.
            return methods !== weakMapMethods || !isFunction$1(obj[forEachName]);
        };
    }

    // In the interest of compact minification, we write
    // each string in the fingerprints only once.
    var forEachName = 'forEach',
        hasName = 'has',
        commonInit = ['clear', 'delete'],
        mapTail = ['get', hasName, 'set'];

    // `Map`, `WeakMap` and `Set` each have slightly different
    // combinations of the above sublists.
    var mapMethods = commonInit.concat(forEachName, mapTail),
        weakMapMethods = commonInit.concat(mapTail),
        setMethods = ['add'].concat(commonInit, forEachName, hasName);

    var isMap = isIE11 ? ie11fingerprint(mapMethods) : tagTester('Map');

    var isWeakMap = isIE11 ? ie11fingerprint(weakMapMethods) : tagTester('WeakMap');

    var isSet = isIE11 ? ie11fingerprint(setMethods) : tagTester('Set');

    var isWeakSet = tagTester('WeakSet');

    // Retrieve the values of an object's properties.
    function values(obj) {
        var _keys = keys(obj);
        var length = _keys.length;
        var values = Array(length);
        for (var i = 0; i < length; i++) {
            values[i] = obj[_keys[i]];
        }
        return values;
    }

    // Convert an object into a list of `[key, value]` pairs.
    // The opposite of `_.object` with one argument.
    function pairs(obj) {
        var _keys = keys(obj);
        var length = _keys.length;
        var pairs = Array(length);
        for (var i = 0; i < length; i++) {
            pairs[i] = [_keys[i], obj[_keys[i]]];
        }
        return pairs;
    }

    // Invert the keys and values of an object. The values must be serializable.
    function invert(obj) {
        var result = {};
        var _keys = keys(obj);
        for (var i = 0, length = _keys.length; i < length; i++) {
            result[obj[_keys[i]]] = _keys[i];
        }
        return result;
    }

    // Return a sorted list of the function names available on the object.
    function functions(obj) {
        var names = [];
        for (var key in obj) {
            if (isFunction$1(obj[key])) names.push(key);
        }
        return names.sort();
    }

    // An internal function for creating assigner functions.
    function createAssigner(keysFunc, defaults) {
        return function(obj) {
            var length = arguments.length;
            if (defaults) obj = Object(obj);
            if (length < 2 || obj == null) return obj;
            for (var index = 1; index < length; index++) {
                var source = arguments[index],
                    keys = keysFunc(source),
                    l = keys.length;
                for (var i = 0; i < l; i++) {
                    var key = keys[i];
                    if (!defaults || obj[key] === void 0) obj[key] = source[key];
                }
            }
            return obj;
        };
    }

    // Extend a given object with all the properties in passed-in object(s).
    var extend = createAssigner(allKeys);

    // Assigns a given object with all the own properties in the passed-in
    // object(s).
    // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
    var extendOwn = createAssigner(keys);

    // Fill in a given object with default properties.
    var defaults = createAssigner(allKeys, true);

    // Create a naked function reference for surrogate-prototype-swapping.
    function ctor() {
        return function(){};
    }

    // An internal function for creating a new object that inherits from another.
    function baseCreate(prototype) {
        if (!isObject(prototype)) return {};
        if (nativeCreate) return nativeCreate(prototype);
        var Ctor = ctor();
        Ctor.prototype = prototype;
        var result = new Ctor;
        Ctor.prototype = null;
        return result;
    }

    // Creates an object that inherits from the given prototype object.
    // If additional properties are provided then they will be added to the
    // created object.
    function create(prototype, props) {
        var result = baseCreate(prototype);
        if (props) extendOwn(result, props);
        return result;
    }

    // Create a (shallow-cloned) duplicate of an object.
    function clone(obj) {
        if (!isObject(obj)) return obj;
        return isArray(obj) ? obj.slice() : extend({}, obj);
    }

    // Invokes `interceptor` with the `obj` and then returns `obj`.
    // The primary purpose of this method is to "tap into" a method chain, in
    // order to perform operations on intermediate results within the chain.
    function tap(obj, interceptor) {
        interceptor(obj);
        return obj;
    }

    // Normalize a (deep) property `path` to array.
    // Like `_.iteratee`, this function can be customized.
    function toPath$1(path) {
        return isArray(path) ? path : [path];
    }
    _$1.toPath = toPath$1;

    // Internal wrapper for `_.toPath` to enable minification.
    // Similar to `cb` for `_.iteratee`.
    function toPath(path) {
        return _$1.toPath(path);
    }

    // Internal function to obtain a nested property in `obj` along `path`.
    function deepGet(obj, path) {
        var length = path.length;
        for (var i = 0; i < length; i++) {
            if (obj == null) return void 0;
            obj = obj[path[i]];
        }
        return length ? obj : void 0;
    }

    // Get the value of the (deep) property on `path` from `object`.
    // If any property in `path` does not exist or if the value is
    // `undefined`, return `defaultValue` instead.
    // The `path` is normalized through `_.toPath`.
    function get(object, path, defaultValue) {
        var value = deepGet(object, toPath(path));
        return isUndefined(value) ? defaultValue : value;
    }

    // Shortcut function for checking if an object has a given property directly on
    // itself (in other words, not on a prototype). Unlike the internal `has`
    // function, this public version can also traverse nested properties.
    function has(obj, path) {
        path = toPath(path);
        var length = path.length;
        for (var i = 0; i < length; i++) {
            var key = path[i];
            if (!has$1(obj, key)) return false;
            obj = obj[key];
        }
        return !!length;
    }

    // Keep the identity function around for default iteratees.
    function identity(value) {
        return value;
    }

    // Returns a predicate for checking whether an object has a given set of
    // `key:value` pairs.
    function matcher(attrs) {
        attrs = extendOwn({}, attrs);
        return function(obj) {
            return isMatch(obj, attrs);
        };
    }

    // Creates a function that, when passed an object, will traverse that object’s
    // properties down the given `path`, specified as an array of keys or indices.
    function property(path) {
        path = toPath(path);
        return function(obj) {
            return deepGet(obj, path);
        };
    }

    // Internal function that returns an efficient (for current engines) version
    // of the passed-in callback, to be repeatedly applied in other Underscore
    // functions.
    function optimizeCb(func, context, argCount) {
        if (context === void 0) return func;
        switch (argCount == null ? 3 : argCount) {
            case 1: return function(value) {
                return func.call(context, value);
            };
            // The 2-argument case is omitted because we’re not using it.
            case 3: return function(value, index, collection) {
                return func.call(context, value, index, collection);
            };
            case 4: return function(accumulator, value, index, collection) {
                return func.call(context, accumulator, value, index, collection);
            };
        }
        return function() {
            return func.apply(context, arguments);
        };
    }

    // An internal function to generate callbacks that can be applied to each
    // element in a collection, returning the desired result — either `_.identity`,
    // an arbitrary callback, a property matcher, or a property accessor.
    function baseIteratee(value, context, argCount) {
        if (value == null) return identity;
        if (isFunction$1(value)) return optimizeCb(value, context, argCount);
        if (isObject(value) && !isArray(value)) return matcher(value);
        return property(value);
    }

    // External wrapper for our callback generator. Users may customize
    // `_.iteratee` if they want additional predicate/iteratee shorthand styles.
    // This abstraction hides the internal-only `argCount` argument.
    function iteratee(value, context) {
        return baseIteratee(value, context, Infinity);
    }
    _$1.iteratee = iteratee;

    // The function we call internally to generate a callback. It invokes
    // `_.iteratee` if overridden, otherwise `baseIteratee`.
    function cb(value, context, argCount) {
        if (_$1.iteratee !== iteratee) return _$1.iteratee(value, context);
        return baseIteratee(value, context, argCount);
    }

    // Returns the results of applying the `iteratee` to each element of `obj`.
    // In contrast to `_.map` it returns an object.
    function mapObject(obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var _keys = keys(obj),
            length = _keys.length,
            results = {};
        for (var index = 0; index < length; index++) {
            var currentKey = _keys[index];
            results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    }

    // Predicate-generating function. Often useful outside of Underscore.
    function noop(){}

    // Generates a function for a given object that returns a given property.
    function propertyOf(obj) {
        if (obj == null) return noop;
        return function(path) {
            return get(obj, path);
        };
    }

    // Run a function **n** times.
    function times(n, iteratee, context) {
        var accum = Array(Math.max(0, n));
        iteratee = optimizeCb(iteratee, context, 1);
        for (var i = 0; i < n; i++) accum[i] = iteratee(i);
        return accum;
    }

    // Return a random integer between `min` and `max` (inclusive).
    function random(min, max) {
        if (max == null) {
            max = min;
            min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
    }

    // A (possibly faster) way to get the current timestamp as an integer.
    var now = Date.now || function() {
        return new Date().getTime();
    };

    // Internal helper to generate functions for escaping and unescaping strings
    // to/from HTML interpolation.
    function createEscaper(map) {
        var escaper = function(match) {
            return map[match];
        };
        // Regexes for identifying a key that needs to be escaped.
        var source = '(?:' + keys(map).join('|') + ')';
        var testRegexp = RegExp(source);
        var replaceRegexp = RegExp(source, 'g');
        return function(string) {
            string = string == null ? '' : '' + string;
            return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
        };
    }

    // Internal list of HTML entities for escaping.
    var escapeMap = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#x27;',
        '`': '&#x60;'
    };

    // Function for escaping strings to HTML interpolation.
    var _escape = createEscaper(escapeMap);

    // Internal list of HTML entities for unescaping.
    var unescapeMap = invert(escapeMap);

    // Function for unescaping strings from HTML interpolation.
    var _unescape = createEscaper(unescapeMap);

    // By default, Underscore uses ERB-style template delimiters. Change the
    // following template settings to use alternative delimiters.
    var templateSettings = _$1.templateSettings = {
        evaluate: /<%([\s\S]+?)%>/g,
        interpolate: /<%=([\s\S]+?)%>/g,
        escape: /<%-([\s\S]+?)%>/g
    };

    // When customizing `_.templateSettings`, if you don't want to define an
    // interpolation, evaluation or escaping regex, we need one that is
    // guaranteed not to match.
    var noMatch = /(.)^/;

    // Certain characters need to be escaped so that they can be put into a
    // string literal.
    var escapes = {
        "'": "'",
        '\\': '\\',
        '\r': 'r',
        '\n': 'n',
        '\u2028': 'u2028',
        '\u2029': 'u2029'
    };

    var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;

    function escapeChar(match) {
        return '\\' + escapes[match];
    }

    // In order to prevent third-party code injection through
    // `_.templateSettings.variable`, we test it against the following regular
    // expression. It is intentionally a bit more liberal than just matching valid
    // identifiers, but still prevents possible loopholes through defaults or
    // destructuring assignment.
    var bareIdentifier = /^\s*(\w|\$)+\s*$/;

    // JavaScript micro-templating, similar to John Resig's implementation.
    // Underscore templating handles arbitrary delimiters, preserves whitespace,
    // and correctly escapes quotes within interpolated code.
    // NB: `oldSettings` only exists for backwards compatibility.
    function template(text, settings, oldSettings) {
        if (!settings && oldSettings) settings = oldSettings;
        settings = defaults({}, settings, _$1.templateSettings);

        // Combine delimiters into one regular expression via alternation.
        var matcher = RegExp([
            (settings.escape || noMatch).source,
            (settings.interpolate || noMatch).source,
            (settings.evaluate || noMatch).source
        ].join('|') + '|$', 'g');

        // Compile the template source, escaping string literals appropriately.
        var index = 0;
        var source = "__p+='";
        text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
            source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
            index = offset + match.length;

            if (escape) {
                source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
            } else if (interpolate) {
                source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
            } else if (evaluate) {
                source += "';\n" + evaluate + "\n__p+='";
            }

            // Adobe VMs need the match returned to produce the correct offset.
            return match;
        });
        source += "';\n";

        var argument = settings.variable;
        if (argument) {
            // Insure against third-party code injection. (CVE-2021-23358)
            if (!bareIdentifier.test(argument)) throw new Error(
                'variable is not a bare identifier: ' + argument
            );
        } else {
            // If a variable is not specified, place data values in local scope.
            source = 'with(obj||{}){\n' + source + '}\n';
            argument = 'obj';
        }

        source = "var __t,__p='',__j=Array.prototype.join," +
            "print=function(){__p+=__j.call(arguments,'');};\n" +
            source + 'return __p;\n';

        var render;
        try {
            render = new Function(argument, '_', source);
        } catch (e) {
            e.source = source;
            throw e;
        }

        var template = function(data) {
            return render.call(this, data, _$1);
        };

        // Provide the compiled source as a convenience for precompilation.
        template.source = 'function(' + argument + '){\n' + source + '}';

        return template;
    }

    // Traverses the children of `obj` along `path`. If a child is a function, it
    // is invoked with its parent as context. Returns the value of the final
    // child, or `fallback` if any child is undefined.
    function result(obj, path, fallback) {
        path = toPath(path);
        var length = path.length;
        if (!length) {
            return isFunction$1(fallback) ? fallback.call(obj) : fallback;
        }
        for (var i = 0; i < length; i++) {
            var prop = obj == null ? void 0 : obj[path[i]];
            if (prop === void 0) {
                prop = fallback;
                i = length; // Ensure we don't continue iterating.
            }
            obj = isFunction$1(prop) ? prop.call(obj) : prop;
        }
        return obj;
    }

    // Generate a unique integer id (unique within the entire client session).
    // Useful for temporary DOM ids.
    var idCounter = 0;
    function uniqueId(prefix) {
        var id = ++idCounter + '';
        return prefix ? prefix + id : id;
    }

    // Start chaining a wrapped Underscore object.
    function chain(obj) {
        var instance = _$1(obj);
        instance._chain = true;
        return instance;
    }

    // Internal function to execute `sourceFunc` bound to `context` with optional
    // `args`. Determines whether to execute a function as a constructor or as a
    // normal function.
    function executeBound(sourceFunc, boundFunc, context, callingContext, args) {
        if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
        var self = baseCreate(sourceFunc.prototype);
        var result = sourceFunc.apply(self, args);
        if (isObject(result)) return result;
        return self;
    }

    // Partially apply a function by creating a version that has had some of its
    // arguments pre-filled, without changing its dynamic `this` context. `_` acts
    // as a placeholder by default, allowing any combination of arguments to be
    // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
    var partial = restArguments(function(func, boundArgs) {
        var placeholder = partial.placeholder;
        var bound = function() {
            var position = 0, length = boundArgs.length;
            var args = Array(length);
            for (var i = 0; i < length; i++) {
                args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
            }
            while (position < arguments.length) args.push(arguments[position++]);
            return executeBound(func, bound, this, this, args);
        };
        return bound;
    });

    partial.placeholder = _$1;

    // Create a function bound to a given object (assigning `this`, and arguments,
    // optionally).
    var bind = restArguments(function(func, context, args) {
        if (!isFunction$1(func)) throw new TypeError('Bind must be called on a function');
        var bound = restArguments(function(callArgs) {
            return executeBound(func, bound, context, this, args.concat(callArgs));
        });
        return bound;
    });

    // Internal helper for collection methods to determine whether a collection
    // should be iterated as an array or as an object.
    // Related: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
    // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
    var isArrayLike = createSizePropertyCheck(getLength);

    // Internal implementation of a recursive `flatten` function.
    function flatten$1(input, depth, strict, output) {
        output = output || [];
        if (!depth && depth !== 0) {
            depth = Infinity;
        } else if (depth <= 0) {
            return output.concat(input);
        }
        var idx = output.length;
        for (var i = 0, length = getLength(input); i < length; i++) {
            var value = input[i];
            if (isArrayLike(value) && (isArray(value) || isArguments$1(value))) {
                // Flatten current level of array or arguments object.
                if (depth > 1) {
                    flatten$1(value, depth - 1, strict, output);
                    idx = output.length;
                } else {
                    var j = 0, len = value.length;
                    while (j < len) output[idx++] = value[j++];
                }
            } else if (!strict) {
                output[idx++] = value;
            }
        }
        return output;
    }

    // Bind a number of an object's methods to that object. Remaining arguments
    // are the method names to be bound. Useful for ensuring that all callbacks
    // defined on an object belong to it.
    var bindAll = restArguments(function(obj, keys) {
        keys = flatten$1(keys, false, false);
        var index = keys.length;
        if (index < 1) throw new Error('bindAll must be passed function names');
        while (index--) {
            var key = keys[index];
            obj[key] = bind(obj[key], obj);
        }
        return obj;
    });

    // Memoize an expensive function by storing its results.
    function memoize(func, hasher) {
        var memoize = function(key) {
            var cache = memoize.cache;
            var address = '' + (hasher ? hasher.apply(this, arguments) : key);
            if (!has$1(cache, address)) cache[address] = func.apply(this, arguments);
            return cache[address];
        };
        memoize.cache = {};
        return memoize;
    }

    // Delays a function for the given number of milliseconds, and then calls
    // it with the arguments supplied.
    var delay = restArguments(function(func, wait, args) {
        return setTimeout(function() {
            return func.apply(null, args);
        }, wait);
    });

    // Defers a function, scheduling it to run after the current call stack has
    // cleared.
    var defer = partial(delay, _$1, 1);

    // Returns a function, that, when invoked, will only be triggered at most once
    // during a given window of time. Normally, the throttled function will run
    // as much as it can, without ever going more than once per `wait` duration;
    // but if you'd like to disable the execution on the leading edge, pass
    // `{leading: false}`. To disable execution on the trailing edge, ditto.
    function throttle(func, wait, options) {
        var timeout, context, args, result;
        var previous = 0;
        if (!options) options = {};

        var later = function() {
            previous = options.leading === false ? 0 : now();
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };

        var throttled = function() {
            var _now = now();
            if (!previous && options.leading === false) previous = _now;
            var remaining = wait - (_now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = _now;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
            return result;
        };

        throttled.cancel = function() {
            clearTimeout(timeout);
            previous = 0;
            timeout = context = args = null;
        };

        return throttled;
    }

    // When a sequence of calls of the returned function ends, the argument
    // function is triggered. The end of a sequence is defined by the `wait`
    // parameter. If `immediate` is passed, the argument function will be
    // triggered at the beginning of the sequence instead of at the end.
    function debounce(func, wait, immediate) {
        var timeout, previous, args, result, context;

        var later = function() {
            var passed = now() - previous;
            if (wait > passed) {
                timeout = setTimeout(later, wait - passed);
            } else {
                timeout = null;
                if (!immediate) result = func.apply(context, args);
                // This check is needed because `func` can recursively invoke `debounced`.
                if (!timeout) args = context = null;
            }
        };

        var debounced = restArguments(function(_args) {
            context = this;
            args = _args;
            previous = now();
            if (!timeout) {
                timeout = setTimeout(later, wait);
                if (immediate) result = func.apply(context, args);
            }
            return result;
        });

        debounced.cancel = function() {
            clearTimeout(timeout);
            timeout = args = context = null;
        };

        return debounced;
    }

    // Returns the first function passed as an argument to the second,
    // allowing you to adjust arguments, run code before and after, and
    // conditionally execute the original function.
    function wrap(func, wrapper) {
        return partial(wrapper, func);
    }

    // Returns a negated version of the passed-in predicate.
    function negate(predicate) {
        return function() {
            return !predicate.apply(this, arguments);
        };
    }

    // Returns a function that is the composition of a list of functions, each
    // consuming the return value of the function that follows.
    function compose() {
        var args = arguments;
        var start = args.length - 1;
        return function() {
            var i = start;
            var result = args[start].apply(this, arguments);
            while (i--) result = args[i].call(this, result);
            return result;
        };
    }

    // Returns a function that will only be executed on and after the Nth call.
    function after(times, func) {
        return function() {
            if (--times < 1) {
                return func.apply(this, arguments);
            }
        };
    }

    // Returns a function that will only be executed up to (but not including) the
    // Nth call.
    function before(times, func) {
        var memo;
        return function() {
            if (--times > 0) {
                memo = func.apply(this, arguments);
            }
            if (times <= 1) func = null;
            return memo;
        };
    }

    // Returns a function that will be executed at most one time, no matter how
    // often you call it. Useful for lazy initialization.
    var once = partial(before, 2);

    // Returns the first key on an object that passes a truth test.
    function findKey(obj, predicate, context) {
        predicate = cb(predicate, context);
        var _keys = keys(obj), key;
        for (var i = 0, length = _keys.length; i < length; i++) {
            key = _keys[i];
            if (predicate(obj[key], key, obj)) return key;
        }
    }

    // Internal function to generate `_.findIndex` and `_.findLastIndex`.
    function createPredicateIndexFinder(dir) {
        return function(array, predicate, context) {
            predicate = cb(predicate, context);
            var length = getLength(array);
            var index = dir > 0 ? 0 : length - 1;
            for (; index >= 0 && index < length; index += dir) {
                if (predicate(array[index], index, array)) return index;
            }
            return -1;
        };
    }

    // Returns the first index on an array-like that passes a truth test.
    var findIndex = createPredicateIndexFinder(1);

    // Returns the last index on an array-like that passes a truth test.
    var findLastIndex = createPredicateIndexFinder(-1);

    // Use a comparator function to figure out the smallest index at which
    // an object should be inserted so as to maintain order. Uses binary search.
    function sortedIndex(array, obj, iteratee, context) {
        iteratee = cb(iteratee, context, 1);
        var value = iteratee(obj);
        var low = 0, high = getLength(array);
        while (low < high) {
            var mid = Math.floor((low + high) / 2);
            if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
        }
        return low;
    }

    // Internal function to generate the `_.indexOf` and `_.lastIndexOf` functions.
    function createIndexFinder(dir, predicateFind, sortedIndex) {
        return function(array, item, idx) {
            var i = 0, length = getLength(array);
            if (typeof idx == 'number') {
                if (dir > 0) {
                    i = idx >= 0 ? idx : Math.max(idx + length, i);
                } else {
                    length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
                }
            } else if (sortedIndex && idx && length) {
                idx = sortedIndex(array, item);
                return array[idx] === item ? idx : -1;
            }
            if (item !== item) {
                idx = predicateFind(slice.call(array, i, length), isNaN$1);
                return idx >= 0 ? idx + i : -1;
            }
            for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
                if (array[idx] === item) return idx;
            }
            return -1;
        };
    }

    // Return the position of the first occurrence of an item in an array,
    // or -1 if the item is not included in the array.
    // If the array is large and already in sort order, pass `true`
    // for **isSorted** to use binary search.
    var indexOf = createIndexFinder(1, findIndex, sortedIndex);

    // Return the position of the last occurrence of an item in an array,
    // or -1 if the item is not included in the array.
    var lastIndexOf = createIndexFinder(-1, findLastIndex);

    // Return the first value which passes a truth test.
    function find(obj, predicate, context) {
        var keyFinder = isArrayLike(obj) ? findIndex : findKey;
        var key = keyFinder(obj, predicate, context);
        if (key !== void 0 && key !== -1) return obj[key];
    }

    // Convenience version of a common use case of `_.find`: getting the first
    // object containing specific `key:value` pairs.
    function findWhere(obj, attrs) {
        return find(obj, matcher(attrs));
    }

    // The cornerstone for collection functions, an `each`
    // implementation, aka `forEach`.
    // Handles raw objects in addition to array-likes. Treats all
    // sparse array-likes as if they were dense.
    function each(obj, iteratee, context) {
        iteratee = optimizeCb(iteratee, context);
        var i, length;
        if (isArrayLike(obj)) {
            for (i = 0, length = obj.length; i < length; i++) {
                iteratee(obj[i], i, obj);
            }
        } else {
            var _keys = keys(obj);
            for (i = 0, length = _keys.length; i < length; i++) {
                iteratee(obj[_keys[i]], _keys[i], obj);
            }
        }
        return obj;
    }

    // Return the results of applying the iteratee to each element.
    function map(obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var _keys = !isArrayLike(obj) && keys(obj),
            length = (_keys || obj).length,
            results = Array(length);
        for (var index = 0; index < length; index++) {
            var currentKey = _keys ? _keys[index] : index;
            results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    }

    // Internal helper to create a reducing function, iterating left or right.
    function createReduce(dir) {
        // Wrap code that reassigns argument variables in a separate function than
        // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
        var reducer = function(obj, iteratee, memo, initial) {
            var _keys = !isArrayLike(obj) && keys(obj),
                length = (_keys || obj).length,
                index = dir > 0 ? 0 : length - 1;
            if (!initial) {
                memo = obj[_keys ? _keys[index] : index];
                index += dir;
            }
            for (; index >= 0 && index < length; index += dir) {
                var currentKey = _keys ? _keys[index] : index;
                memo = iteratee(memo, obj[currentKey], currentKey, obj);
            }
            return memo;
        };

        return function(obj, iteratee, memo, context) {
            var initial = arguments.length >= 3;
            return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
        };
    }

    // **Reduce** builds up a single result from a list of values, aka `inject`,
    // or `foldl`.
    var reduce = createReduce(1);

    // The right-associative version of reduce, also known as `foldr`.
    var reduceRight = createReduce(-1);

    // Return all the elements that pass a truth test.
    function filter(obj, predicate, context) {
        var results = [];
        predicate = cb(predicate, context);
        each(obj, function(value, index, list) {
            if (predicate(value, index, list)) results.push(value);
        });
        return results;
    }

    // Return all the elements for which a truth test fails.
    function reject(obj, predicate, context) {
        return filter(obj, negate(cb(predicate)), context);
    }

    // Determine whether all of the elements pass a truth test.
    function every(obj, predicate, context) {
        predicate = cb(predicate, context);
        var _keys = !isArrayLike(obj) && keys(obj),
            length = (_keys || obj).length;
        for (var index = 0; index < length; index++) {
            var currentKey = _keys ? _keys[index] : index;
            if (!predicate(obj[currentKey], currentKey, obj)) return false;
        }
        return true;
    }

    // Determine if at least one element in the object passes a truth test.
    function some(obj, predicate, context) {
        predicate = cb(predicate, context);
        var _keys = !isArrayLike(obj) && keys(obj),
            length = (_keys || obj).length;
        for (var index = 0; index < length; index++) {
            var currentKey = _keys ? _keys[index] : index;
            if (predicate(obj[currentKey], currentKey, obj)) return true;
        }
        return false;
    }

    // Determine if the array or object contains a given item (using `===`).
    function contains(obj, item, fromIndex, guard) {
        if (!isArrayLike(obj)) obj = values(obj);
        if (typeof fromIndex != 'number' || guard) fromIndex = 0;
        return indexOf(obj, item, fromIndex) >= 0;
    }

    // Invoke a method (with arguments) on every item in a collection.
    var invoke = restArguments(function(obj, path, args) {
        var contextPath, func;
        if (isFunction$1(path)) {
            func = path;
        } else {
            path = toPath(path);
            contextPath = path.slice(0, -1);
            path = path[path.length - 1];
        }
        return map(obj, function(context) {
            var method = func;
            if (!method) {
                if (contextPath && contextPath.length) {
                    context = deepGet(context, contextPath);
                }
                if (context == null) return void 0;
                method = context[path];
            }
            return method == null ? method : method.apply(context, args);
        });
    });

    // Convenience version of a common use case of `_.map`: fetching a property.
    function pluck(obj, key) {
        return map(obj, property(key));
    }

    // Convenience version of a common use case of `_.filter`: selecting only
    // objects containing specific `key:value` pairs.
    function where(obj, attrs) {
        return filter(obj, matcher(attrs));
    }

    // Return the maximum element (or element-based computation).
    function max(obj, iteratee, context) {
        var result = -Infinity, lastComputed = -Infinity,
            value, computed;
        if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
            obj = isArrayLike(obj) ? obj : values(obj);
            for (var i = 0, length = obj.length; i < length; i++) {
                value = obj[i];
                if (value != null && value > result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            each(obj, function(v, index, list) {
                computed = iteratee(v, index, list);
                if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                    result = v;
                    lastComputed = computed;
                }
            });
        }
        return result;
    }

    // Return the minimum element (or element-based computation).
    function min(obj, iteratee, context) {
        var result = Infinity, lastComputed = Infinity,
            value, computed;
        if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
            obj = isArrayLike(obj) ? obj : values(obj);
            for (var i = 0, length = obj.length; i < length; i++) {
                value = obj[i];
                if (value != null && value < result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            each(obj, function(v, index, list) {
                computed = iteratee(v, index, list);
                if (computed < lastComputed || computed === Infinity && result === Infinity) {
                    result = v;
                    lastComputed = computed;
                }
            });
        }
        return result;
    }

    // Safely create a real, live array from anything iterable.
    var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
    function toArray(obj) {
        if (!obj) return [];
        if (isArray(obj)) return slice.call(obj);
        if (isString(obj)) {
            // Keep surrogate pair characters together.
            return obj.match(reStrSymbol);
        }
        if (isArrayLike(obj)) return map(obj, identity);
        return values(obj);
    }

    // Sample **n** random values from a collection using the modern version of the
    // [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
    // If **n** is not specified, returns a single random element.
    // The internal `guard` argument allows it to work with `_.map`.
    function sample(obj, n, guard) {
        if (n == null || guard) {
            if (!isArrayLike(obj)) obj = values(obj);
            return obj[random(obj.length - 1)];
        }
        var sample = toArray(obj);
        var length = getLength(sample);
        n = Math.max(Math.min(n, length), 0);
        var last = length - 1;
        for (var index = 0; index < n; index++) {
            var rand = random(index, last);
            var temp = sample[index];
            sample[index] = sample[rand];
            sample[rand] = temp;
        }
        return sample.slice(0, n);
    }

    // Shuffle a collection.
    function shuffle(obj) {
        return sample(obj, Infinity);
    }

    // Sort the object's values by a criterion produced by an iteratee.
    function sortBy(obj, iteratee, context) {
        var index = 0;
        iteratee = cb(iteratee, context);
        return pluck(map(obj, function(value, key, list) {
            return {
                value: value,
                index: index++,
                criteria: iteratee(value, key, list)
            };
        }).sort(function(left, right) {
            var a = left.criteria;
            var b = right.criteria;
            if (a !== b) {
                if (a > b || a === void 0) return 1;
                if (a < b || b === void 0) return -1;
            }
            return left.index - right.index;
        }), 'value');
    }

    // An internal function used for aggregate "group by" operations.
    function group(behavior, partition) {
        return function(obj, iteratee, context) {
            var result = partition ? [[], []] : {};
            iteratee = cb(iteratee, context);
            each(obj, function(value, index) {
                var key = iteratee(value, index, obj);
                behavior(result, value, key);
            });
            return result;
        };
    }

    // Groups the object's values by a criterion. Pass either a string attribute
    // to group by, or a function that returns the criterion.
    var groupBy = group(function(result, value, key) {
        if (has$1(result, key)) result[key].push(value); else result[key] = [value];
    });

    // Indexes the object's values by a criterion, similar to `_.groupBy`, but for
    // when you know that your index values will be unique.
    var indexBy = group(function(result, value, key) {
        result[key] = value;
    });

    // Counts instances of an object that group by a certain criterion. Pass
    // either a string attribute to count by, or a function that returns the
    // criterion.
    var countBy = group(function(result, value, key) {
        if (has$1(result, key)) result[key]++; else result[key] = 1;
    });

    // Split a collection into two arrays: one whose elements all pass the given
    // truth test, and one whose elements all do not pass the truth test.
    var partition = group(function(result, value, pass) {
        result[pass ? 0 : 1].push(value);
    }, true);

    // Return the number of elements in a collection.
    function size(obj) {
        if (obj == null) return 0;
        return isArrayLike(obj) ? obj.length : keys(obj).length;
    }

    // Internal `_.pick` helper function to determine whether `key` is an enumerable
    // property name of `obj`.
    function keyInObj(value, key, obj) {
        return key in obj;
    }

    // Return a copy of the object only containing the allowed properties.
    var pick = restArguments(function(obj, keys) {
        var result = {}, iteratee = keys[0];
        if (obj == null) return result;
        if (isFunction$1(iteratee)) {
            if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
            keys = allKeys(obj);
        } else {
            iteratee = keyInObj;
            keys = flatten$1(keys, false, false);
            obj = Object(obj);
        }
        for (var i = 0, length = keys.length; i < length; i++) {
            var key = keys[i];
            var value = obj[key];
            if (iteratee(value, key, obj)) result[key] = value;
        }
        return result;
    });

    // Return a copy of the object without the disallowed properties.
    var omit = restArguments(function(obj, keys) {
        var iteratee = keys[0], context;
        if (isFunction$1(iteratee)) {
            iteratee = negate(iteratee);
            if (keys.length > 1) context = keys[1];
        } else {
            keys = map(flatten$1(keys, false, false), String);
            iteratee = function(value, key) {
                return !contains(keys, key);
            };
        }
        return pick(obj, iteratee, context);
    });

    // Returns everything but the last entry of the array. Especially useful on
    // the arguments object. Passing **n** will return all the values in
    // the array, excluding the last N.
    function initial(array, n, guard) {
        return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
    }

    // Get the first element of an array. Passing **n** will return the first N
    // values in the array. The **guard** check allows it to work with `_.map`.
    function first(array, n, guard) {
        if (array == null || array.length < 1) return n == null || guard ? void 0 : [];
        if (n == null || guard) return array[0];
        return initial(array, array.length - n);
    }

    // Returns everything but the first entry of the `array`. Especially useful on
    // the `arguments` object. Passing an **n** will return the rest N values in the
    // `array`.
    function rest(array, n, guard) {
        return slice.call(array, n == null || guard ? 1 : n);
    }

    // Get the last element of an array. Passing **n** will return the last N
    // values in the array.
    function last(array, n, guard) {
        if (array == null || array.length < 1) return n == null || guard ? void 0 : [];
        if (n == null || guard) return array[array.length - 1];
        return rest(array, Math.max(0, array.length - n));
    }

    // Trim out all falsy values from an array.
    function compact(array) {
        return filter(array, Boolean);
    }

    // Flatten out an array, either recursively (by default), or up to `depth`.
    // Passing `true` or `false` as `depth` means `1` or `Infinity`, respectively.
    function flatten(array, depth) {
        return flatten$1(array, depth, false);
    }

    // Take the difference between one array and a number of other arrays.
    // Only the elements present in just the first array will remain.
    var difference = restArguments(function(array, rest) {
        rest = flatten$1(rest, true, true);
        return filter(array, function(value){
            return !contains(rest, value);
        });
    });

    // Return a version of the array that does not contain the specified value(s).
    var without = restArguments(function(array, otherArrays) {
        return difference(array, otherArrays);
    });

    // Produce a duplicate-free version of the array. If the array has already
    // been sorted, you have the option of using a faster algorithm.
    // The faster algorithm will not work with an iteratee if the iteratee
    // is not a one-to-one function, so providing an iteratee will disable
    // the faster algorithm.
    function uniq(array, isSorted, iteratee, context) {
        if (!isBoolean(isSorted)) {
            context = iteratee;
            iteratee = isSorted;
            isSorted = false;
        }
        if (iteratee != null) iteratee = cb(iteratee, context);
        var result = [];
        var seen = [];
        for (var i = 0, length = getLength(array); i < length; i++) {
            var value = array[i],
                computed = iteratee ? iteratee(value, i, array) : value;
            if (isSorted && !iteratee) {
                if (!i || seen !== computed) result.push(value);
                seen = computed;
            } else if (iteratee) {
                if (!contains(seen, computed)) {
                    seen.push(computed);
                    result.push(value);
                }
            } else if (!contains(result, value)) {
                result.push(value);
            }
        }
        return result;
    }

    // Produce an array that contains the union: each distinct element from all of
    // the passed-in arrays.
    var union = restArguments(function(arrays) {
        return uniq(flatten$1(arrays, true, true));
    });

    // Produce an array that contains every item shared between all the
    // passed-in arrays.
    function intersection(array) {
        var result = [];
        var argsLength = arguments.length;
        for (var i = 0, length = getLength(array); i < length; i++) {
            var item = array[i];
            if (contains(result, item)) continue;
            var j;
            for (j = 1; j < argsLength; j++) {
                if (!contains(arguments[j], item)) break;
            }
            if (j === argsLength) result.push(item);
        }
        return result;
    }

    // Complement of zip. Unzip accepts an array of arrays and groups
    // each array's elements on shared indices.
    function unzip(array) {
        var length = array && max(array, getLength).length || 0;
        var result = Array(length);

        for (var index = 0; index < length; index++) {
            result[index] = pluck(array, index);
        }
        return result;
    }

    // Zip together multiple lists into a single array -- elements that share
    // an index go together.
    var zip = restArguments(unzip);

    // Converts lists into objects. Pass either a single array of `[key, value]`
    // pairs, or two parallel arrays of the same length -- one of keys, and one of
    // the corresponding values. Passing by pairs is the reverse of `_.pairs`.
    function object(list, values) {
        var result = {};
        for (var i = 0, length = getLength(list); i < length; i++) {
            if (values) {
                result[list[i]] = values[i];
            } else {
                result[list[i][0]] = list[i][1];
            }
        }
        return result;
    }

    // Generate an integer Array containing an arithmetic progression. A port of
    // the native Python `range()` function. See
    // [the Python documentation](https://docs.python.org/library/functions.html#range).
    function range(start, stop, step) {
        if (stop == null) {
            stop = start || 0;
            start = 0;
        }
        if (!step) {
            step = stop < start ? -1 : 1;
        }

        var length = Math.max(Math.ceil((stop - start) / step), 0);
        var range = Array(length);

        for (var idx = 0; idx < length; idx++, start += step) {
            range[idx] = start;
        }

        return range;
    }

    // Chunk a single array into multiple arrays, each containing `count` or fewer
    // items.
    function chunk(array, count) {
        if (count == null || count < 1) return [];
        var result = [];
        var i = 0, length = array.length;
        while (i < length) {
            result.push(slice.call(array, i, i += count));
        }
        return result;
    }

    // Helper function to continue chaining intermediate results.
    function chainResult(instance, obj) {
        return instance._chain ? _$1(obj).chain() : obj;
    }

    // Add your own custom functions to the Underscore object.
    function mixin(obj) {
        each(functions(obj), function(name) {
            var func = _$1[name] = obj[name];
            _$1.prototype[name] = function() {
                var args = [this._wrapped];
                push.apply(args, arguments);
                return chainResult(this, func.apply(_$1, args));
            };
        });
        return _$1;
    }

    // Add all mutator `Array` functions to the wrapper.
    each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
        var method = ArrayProto[name];
        _$1.prototype[name] = function() {
            var obj = this._wrapped;
            if (obj != null) {
                method.apply(obj, arguments);
                if ((name === 'shift' || name === 'splice') && obj.length === 0) {
                    delete obj[0];
                }
            }
            return chainResult(this, obj);
        };
    });

    // Add all accessor `Array` functions to the wrapper.
    each(['concat', 'join', 'slice'], function(name) {
        var method = ArrayProto[name];
        _$1.prototype[name] = function() {
            var obj = this._wrapped;
            if (obj != null) obj = method.apply(obj, arguments);
            return chainResult(this, obj);
        };
    });

    // Named Exports

    var allExports = {
        __proto__: null,
        VERSION: VERSION,
        restArguments: restArguments,
        isObject: isObject,
        isNull: isNull,
        isUndefined: isUndefined,
        isBoolean: isBoolean,
        isElement: isElement,
        isString: isString,
        isNumber: isNumber,
        isDate: isDate,
        isRegExp: isRegExp,
        isError: isError,
        isSymbol: isSymbol,
        isArrayBuffer: isArrayBuffer,
        isDataView: isDataView$1,
        isArray: isArray,
        isFunction: isFunction$1,
        isArguments: isArguments$1,
        isFinite: isFinite$1,
        isNaN: isNaN$1,
        isTypedArray: isTypedArray$1,
        isEmpty: isEmpty,
        isMatch: isMatch,
        isEqual: isEqual,
        isMap: isMap,
        isWeakMap: isWeakMap,
        isSet: isSet,
        isWeakSet: isWeakSet,
        keys: keys,
        allKeys: allKeys,
        values: values,
        pairs: pairs,
        invert: invert,
        functions: functions,
        methods: functions,
        extend: extend,
        extendOwn: extendOwn,
        assign: extendOwn,
        defaults: defaults,
        create: create,
        clone: clone,
        tap: tap,
        get: get,
        has: has,
        mapObject: mapObject,
        identity: identity,
        constant: constant,
        noop: noop,
        toPath: toPath$1,
        property: property,
        propertyOf: propertyOf,
        matcher: matcher,
        matches: matcher,
        times: times,
        random: random,
        now: now,
        escape: _escape,
        unescape: _unescape,
        templateSettings: templateSettings,
        template: template,
        result: result,
        uniqueId: uniqueId,
        chain: chain,
        iteratee: iteratee,
        partial: partial,
        bind: bind,
        bindAll: bindAll,
        memoize: memoize,
        delay: delay,
        defer: defer,
        throttle: throttle,
        debounce: debounce,
        wrap: wrap,
        negate: negate,
        compose: compose,
        after: after,
        before: before,
        once: once,
        findKey: findKey,
        findIndex: findIndex,
        findLastIndex: findLastIndex,
        sortedIndex: sortedIndex,
        indexOf: indexOf,
        lastIndexOf: lastIndexOf,
        find: find,
        detect: find,
        findWhere: findWhere,
        each: each,
        forEach: each,
        map: map,
        collect: map,
        reduce: reduce,
        foldl: reduce,
        inject: reduce,
        reduceRight: reduceRight,
        foldr: reduceRight,
        filter: filter,
        select: filter,
        reject: reject,
        every: every,
        all: every,
        some: some,
        any: some,
        contains: contains,
        includes: contains,
        include: contains,
        invoke: invoke,
        pluck: pluck,
        where: where,
        max: max,
        min: min,
        shuffle: shuffle,
        sample: sample,
        sortBy: sortBy,
        groupBy: groupBy,
        indexBy: indexBy,
        countBy: countBy,
        partition: partition,
        toArray: toArray,
        size: size,
        pick: pick,
        omit: omit,
        first: first,
        head: first,
        take: first,
        initial: initial,
        last: last,
        rest: rest,
        tail: rest,
        drop: rest,
        compact: compact,
        flatten: flatten,
        without: without,
        uniq: uniq,
        unique: uniq,
        union: union,
        intersection: intersection,
        difference: difference,
        unzip: unzip,
        transpose: unzip,
        zip: zip,
        object: object,
        range: range,
        chunk: chunk,
        mixin: mixin,
        'default': _$1
    };

    // Default Export

    // Add all of the Underscore functions to the wrapper object.
    var _ = mixin(allExports);
    // Legacy Node.js API.
    _._ = _;

    return _;

})));

/**
 * 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;
        }
    };
});

/*!
 * Knockout JavaScript library v3.5.1
 * (c) The Knockout.js team - http://knockoutjs.com/
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

(function(){
    var DEBUG=true;
    (function(undefined){
        // (0, eval)('this') is a robust way of getting a reference to the global object
        // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023
        var window = this || (0, eval)('this'),
            document = window['document'],
            navigator = window['navigator'],
            jQueryInstance = window["jQuery"],
            JSON = window["JSON"];

        if (!jQueryInstance && typeof jQuery !== "undefined") {
            jQueryInstance = jQuery;
        }
        (function(factory) {
            // Support three module loading scenarios
            if (typeof define === 'function' && define['amd']) {
                // [1] AMD anonymous module
                define('knockoutjs/knockout',['exports', 'require'], factory);
            } else if (typeof exports === 'object' && typeof module === 'object') {
                // [2] CommonJS/Node.js
                factory(module['exports'] || exports);  // module.exports is for Node.js
            } else {
                // [3] No module loader (plain <script> tag) - put directly in global namespace
                factory(window['ko'] = {});
            }
        }(function(koExports, amdRequire){
// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
            var ko = typeof koExports !== 'undefined' ? koExports : {};
// Google Closure Compiler helpers (used only to make the minified file smaller)
            ko.exportSymbol = function(koPath, object) {
                var tokens = koPath.split(".");

                // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
                // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
                var target = ko;

                for (var i = 0; i < tokens.length - 1; i++)
                    target = target[tokens[i]];
                target[tokens[tokens.length - 1]] = object;
            };
            ko.exportProperty = function(owner, publicName, object) {
                owner[publicName] = object;
            };
            ko.version = "3.5.1";

            ko.exportSymbol('version', ko.version);
// For any options that may affect various areas of Knockout and aren't directly associated with data binding.
            ko.options = {
                'deferUpdates': false,
                'useOnlyNativeEvents': false,
                'foreachHidesDestroyed': false
            };

//ko.exportSymbol('options', ko.options);   // 'options' isn't minified
            ko.utils = (function () {
                var hasOwnProperty = Object.prototype.hasOwnProperty;

                function objectForEach(obj, action) {
                    for (var prop in obj) {
                        if (hasOwnProperty.call(obj, prop)) {
                            action(prop, obj[prop]);
                        }
                    }
                }

                function extend(target, source) {
                    if (source) {
                        for(var prop in source) {
                            if(hasOwnProperty.call(source, prop)) {
                                target[prop] = source[prop];
                            }
                        }
                    }
                    return target;
                }

                function setPrototypeOf(obj, proto) {
                    obj.__proto__ = proto;
                    return obj;
                }

                var canSetPrototype = ({ __proto__: [] } instanceof Array);
                var canUseSymbols = !DEBUG && typeof Symbol === 'function';

                // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
                var knownEvents = {}, knownEventTypesByEventName = {};
                var keyEventTypeName = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
                knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
                knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
                objectForEach(knownEvents, function(eventType, knownEventsForType) {
                    if (knownEventsForType.length) {
                        for (var i = 0, j = knownEventsForType.length; i < j; i++)
                            knownEventTypesByEventName[knownEventsForType[i]] = eventType;
                    }
                });
                var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406

                // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
                // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
                // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
                // If there is a future need to detect specific versions of IE10+, we will amend this.
                var ieVersion = document && (function() {
                    var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');

                    // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
                    while (
                        div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
                            iElems[0]
                        ) {}
                    return version > 4 ? version : undefined;
                }());
                var isIe6 = ieVersion === 6,
                    isIe7 = ieVersion === 7;

                function isClickOnCheckableElement(element, eventType) {
                    if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
                    if (eventType.toLowerCase() != "click") return false;
                    var inputType = element.type;
                    return (inputType == "checkbox") || (inputType == "radio");
                }

                // For details on the pattern for changing node classes
                // see: https://github.com/knockout/knockout/issues/1597
                var cssClassNameRegex = /\S+/g;

                var jQueryEventAttachName;

                function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
                    var addOrRemoveFn;
                    if (classNames) {
                        if (typeof node.classList === 'object') {
                            addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
                            ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
                                addOrRemoveFn.call(node.classList, className);
                            });
                        } else if (typeof node.className['baseVal'] === 'string') {
                            // SVG tag .classNames is an SVGAnimatedString instance
                            toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
                        } else {
                            // node.className ought to be a string.
                            toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
                        }
                    }
                }

                function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {
                    // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
                    var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
                    ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
                        ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
                    });
                    obj[prop] = currentClassNames.join(" ");
                }

                return {
                    fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],

                    arrayForEach: function (array, action, actionOwner) {
                        for (var i = 0, j = array.length; i < j; i++) {
                            action.call(actionOwner, array[i], i, array);
                        }
                    },

                    arrayIndexOf: typeof Array.prototype.indexOf == "function"
                        ? function (array, item) {
                            return Array.prototype.indexOf.call(array, item);
                        }
                        : function (array, item) {
                            for (var i = 0, j = array.length; i < j; i++) {
                                if (array[i] === item)
                                    return i;
                            }
                            return -1;
                        },

                    arrayFirst: function (array, predicate, predicateOwner) {
                        for (var i = 0, j = array.length; i < j; i++) {
                            if (predicate.call(predicateOwner, array[i], i, array))
                                return array[i];
                        }
                        return undefined;
                    },

                    arrayRemoveItem: function (array, itemToRemove) {
                        var index = ko.utils.arrayIndexOf(array, itemToRemove);
                        if (index > 0) {
                            array.splice(index, 1);
                        }
                        else if (index === 0) {
                            array.shift();
                        }
                    },

                    arrayGetDistinctValues: function (array) {
                        var result = [];
                        if (array) {
                            ko.utils.arrayForEach(array, function(item) {
                                if (ko.utils.arrayIndexOf(result, item) < 0)
                                    result.push(item);
                            });
                        }
                        return result;
                    },

                    arrayMap: function (array, mapping, mappingOwner) {
                        var result = [];
                        if (array) {
                            for (var i = 0, j = array.length; i < j; i++)
                                result.push(mapping.call(mappingOwner, array[i], i));
                        }
                        return result;
                    },

                    arrayFilter: function (array, predicate, predicateOwner) {
                        var result = [];
                        if (array) {
                            for (var i = 0, j = array.length; i < j; i++)
                                if (predicate.call(predicateOwner, array[i], i))
                                    result.push(array[i]);
                        }
                        return result;
                    },

                    arrayPushAll: function (array, valuesToPush) {
                        if (valuesToPush instanceof Array)
                            array.push.apply(array, valuesToPush);
                        else
                            for (var i = 0, j = valuesToPush.length; i < j; i++)
                                array.push(valuesToPush[i]);
                        return array;
                    },

                    addOrRemoveItem: function(array, value, included) {
                        var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);
                        if (existingEntryIndex < 0) {
                            if (included)
                                array.push(value);
                        } else {
                            if (!included)
                                array.splice(existingEntryIndex, 1);
                        }
                    },

                    canSetPrototype: canSetPrototype,

                    extend: extend,

                    setPrototypeOf: setPrototypeOf,

                    setPrototypeOfOrExtend: canSetPrototype ? setPrototypeOf : extend,

                    objectForEach: objectForEach,

                    objectMap: function(source, mapping, mappingOwner) {
                        if (!source)
                            return source;
                        var target = {};
                        for (var prop in source) {
                            if (hasOwnProperty.call(source, prop)) {
                                target[prop] = mapping.call(mappingOwner, source[prop], prop, source);
                            }
                        }
                        return target;
                    },

                    emptyDomNode: function (domNode) {
                        while (domNode.firstChild) {
                            ko.removeNode(domNode.firstChild);
                        }
                    },

                    moveCleanedNodesToContainerElement: function(nodes) {
                        // Ensure it's a real array, as we're about to reparent the nodes and
                        // we don't want the underlying collection to change while we're doing that.
                        var nodesArray = ko.utils.makeArray(nodes);
                        var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;

                        var container = templateDocument.createElement('div');
                        for (var i = 0, j = nodesArray.length; i < j; i++) {
                            container.appendChild(ko.cleanNode(nodesArray[i]));
                        }
                        return container;
                    },

                    cloneNodes: function (nodesArray, shouldCleanNodes) {
                        for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
                            var clonedNode = nodesArray[i].cloneNode(true);
                            newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
                        }
                        return newNodesArray;
                    },

                    setDomNodeChildren: function (domNode, childNodes) {
                        ko.utils.emptyDomNode(domNode);
                        if (childNodes) {
                            for (var i = 0, j = childNodes.length; i < j; i++)
                                domNode.appendChild(childNodes[i]);
                        }
                    },

                    replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
                        var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
                        if (nodesToReplaceArray.length > 0) {
                            var insertionPoint = nodesToReplaceArray[0];
                            var parent = insertionPoint.parentNode;
                            for (var i = 0, j = newNodesArray.length; i < j; i++)
                                parent.insertBefore(newNodesArray[i], insertionPoint);
                            for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
                                ko.removeNode(nodesToReplaceArray[i]);
                            }
                        }
                    },

                    fixUpContinuousNodeArray: function(continuousNodeArray, parentNode) {
                        // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile
                        // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that
                        // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
                        // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
                        // So, this function translates the old "map" output array into its best guess of the set of current DOM nodes.
                        //
                        // Rules:
                        //   [A] Any leading nodes that have been removed should be ignored
                        //       These most likely correspond to memoization nodes that were already removed during binding
                        //       See https://github.com/knockout/knockout/pull/440
                        //   [B] Any trailing nodes that have been remove should be ignored
                        //       This prevents the code here from adding unrelated nodes to the array while processing rule [C]
                        //       See https://github.com/knockout/knockout/pull/1903
                        //   [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,
                        //       and include any nodes that have been inserted among the previous collection

                        if (continuousNodeArray.length) {
                            // The parent node can be a virtual element; so get the real parent node
                            parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode;

                            // Rule [A]
                            while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode)
                                continuousNodeArray.splice(0, 1);

                            // Rule [B]
                            while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
                                continuousNodeArray.length--;

                            // Rule [C]
                            if (continuousNodeArray.length > 1) {
                                var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];
                                // Replace with the actual new continuous node set
                                continuousNodeArray.length = 0;
                                while (current !== last) {
                                    continuousNodeArray.push(current);
                                    current = current.nextSibling;
                                }
                                continuousNodeArray.push(last);
                            }
                        }
                        return continuousNodeArray;
                    },

                    setOptionNodeSelectionState: function (optionNode, isSelected) {
                        // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
                        if (ieVersion < 7)
                            optionNode.setAttribute("selected", isSelected);
                        else
                            optionNode.selected = isSelected;
                    },

                    stringTrim: function (string) {
                        return string === null || string === undefined ? '' :
                            string.trim ?
                                string.trim() :
                                string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
                    },

                    stringStartsWith: function (string, startsWith) {
                        string = string || "";
                        if (startsWith.length > string.length)
                            return false;
                        return string.substring(0, startsWith.length) === startsWith;
                    },

                    domNodeIsContainedBy: function (node, containedByNode) {
                        if (node === containedByNode)
                            return true;
                        if (node.nodeType === 11)
                            return false; // Fixes issue #1162 - can't use node.contains for document fragments on IE8
                        if (containedByNode.contains)
                            return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);
                        if (containedByNode.compareDocumentPosition)
                            return (containedByNode.compareDocumentPosition(node) & 16) == 16;
                        while (node && node != containedByNode) {
                            node = node.parentNode;
                        }
                        return !!node;
                    },

                    domNodeIsAttachedToDocument: function (node) {
                        return ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement);
                    },

                    anyDomNodeIsAttachedToDocument: function(nodes) {
                        return !!ko.utils.arrayFirst(nodes, ko.utils.domNodeIsAttachedToDocument);
                    },

                    tagNameLower: function(element) {
                        // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
                        // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
                        // we don't need to do the .toLowerCase() as it will always be lower case anyway.
                        return element && element.tagName && element.tagName.toLowerCase();
                    },

                    catchFunctionErrors: function (delegate) {
                        return ko['onError'] ? function () {
                            try {
                                return delegate.apply(this, arguments);
                            } catch (e) {
                                ko['onError'] && ko['onError'](e);
                                throw e;
                            }
                        } : delegate;
                    },

                    setTimeout: function (handler, timeout) {
                        return setTimeout(ko.utils.catchFunctionErrors(handler), timeout);
                    },

                    deferError: function (error) {
                        setTimeout(function () {
                            ko['onError'] && ko['onError'](error);
                            throw error;
                        }, 0);
                    },

                    registerEventHandler: function (element, eventType, handler) {
                        var wrappedHandler = ko.utils.catchFunctionErrors(handler);

                        var mustUseAttachEvent = eventsThatMustBeRegisteredUsingAttachEvent[eventType];
                        if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {
                            if (!jQueryEventAttachName) {
                                jQueryEventAttachName = (typeof jQueryInstance(element)['on'] == 'function') ? 'on' : 'bind';
                            }
                            jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);
                        } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
                            element.addEventListener(eventType, wrappedHandler, false);
                        else if (typeof element.attachEvent != "undefined") {
                            var attachEventHandler = function (event) { wrappedHandler.call(element, event); },
                                attachEventName = "on" + eventType;
                            element.attachEvent(attachEventName, attachEventHandler);

                            // IE does not dispose attachEvent handlers automatically (unlike with addEventListener)
                            // so to avoid leaks, we have to remove them manually. See bug #856
                            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                                element.detachEvent(attachEventName, attachEventHandler);
                            });
                        } else
                            throw new Error("Browser doesn't support addEventListener or attachEvent");
                    },

                    triggerEvent: function (element, eventType) {
                        if (!(element && element.nodeType))
                            throw new Error("element must be a DOM node when calling triggerEvent");

                        // For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the
                        // event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)
                        // IE doesn't change the checked state when you trigger the click event using "fireEvent".
                        // In both cases, we'll use the click method instead.
                        var useClickWorkaround = isClickOnCheckableElement(element, eventType);

                        if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {
                            jQueryInstance(element)['trigger'](eventType);
                        } else if (typeof document.createEvent == "function") {
                            if (typeof element.dispatchEvent == "function") {
                                var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
                                var event = document.createEvent(eventCategory);
                                event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
                                element.dispatchEvent(event);
                            }
                            else
                                throw new Error("The supplied element doesn't support dispatchEvent");
                        } else if (useClickWorkaround && element.click) {
                            element.click();
                        } else if (typeof element.fireEvent != "undefined") {
                            element.fireEvent("on" + eventType);
                        } else {
                            throw new Error("Browser doesn't support triggering events");
                        }
                    },

                    unwrapObservable: function (value) {
                        return ko.isObservable(value) ? value() : value;
                    },

                    peekObservable: function (value) {
                        return ko.isObservable(value) ? value.peek() : value;
                    },

                    toggleDomNodeCssClass: toggleDomNodeCssClass,

                    setTextContent: function(element, textContent) {
                        var value = ko.utils.unwrapObservable(textContent);
                        if ((value === null) || (value === undefined))
                            value = "";

                        // We need there to be exactly one child: a text node.
                        // If there are no children, more than one, or if it's not a text node,
                        // we'll clear everything and create a single text node.
                        var innerTextNode = ko.virtualElements.firstChild(element);
                        if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
                            ko.virtualElements.setDomNodeChildren(element, [element.ownerDocument.createTextNode(value)]);
                        } else {
                            innerTextNode.data = value;
                        }

                        ko.utils.forceRefresh(element);
                    },

                    setElementName: function(element, name) {
                        element.name = name;

                        // Workaround IE 6/7 issue
                        // - https://github.com/SteveSanderson/knockout/issues/197
                        // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
                        if (ieVersion <= 7) {
                            try {
                                var escapedName = element.name.replace(/[&<>'"]/g, function(r){ return "&#" + r.charCodeAt(0) + ";"; });
                                element.mergeAttributes(document.createElement("<input name='" + escapedName + "'/>"), false);
                            }
                            catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
                        }
                    },

                    forceRefresh: function(node) {
                        // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
                        if (ieVersion >= 9) {
                            // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
                            var elem = node.nodeType == 1 ? node : node.parentNode;
                            if (elem.style)
                                elem.style.zoom = elem.style.zoom;
                        }
                    },

                    ensureSelectElementIsRenderedCorrectly: function(selectElement) {
                        // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
                        // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
                        // Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)
                        if (ieVersion) {
                            var originalWidth = selectElement.style.width;
                            selectElement.style.width = 0;
                            selectElement.style.width = originalWidth;
                        }
                    },

                    range: function (min, max) {
                        min = ko.utils.unwrapObservable(min);
                        max = ko.utils.unwrapObservable(max);
                        var result = [];
                        for (var i = min; i <= max; i++)
                            result.push(i);
                        return result;
                    },

                    makeArray: function(arrayLikeObject) {
                        var result = [];
                        for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
                            result.push(arrayLikeObject[i]);
                        };
                        return result;
                    },

                    createSymbolOrString: function(identifier) {
                        return canUseSymbols ? Symbol(identifier) : identifier;
                    },

                    isIe6 : isIe6,
                    isIe7 : isIe7,
                    ieVersion : ieVersion,

                    getFormFields: function(form, fieldName) {
                        var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
                        var isMatchingField = (typeof fieldName == 'string')
                            ? function(field) { return field.name === fieldName }
                            : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
                        var matches = [];
                        for (var i = fields.length - 1; i >= 0; i--) {
                            if (isMatchingField(fields[i]))
                                matches.push(fields[i]);
                        };
                        return matches;
                    },

                    parseJson: function (jsonString) {
                        if (typeof jsonString == "string") {
                            jsonString = ko.utils.stringTrim(jsonString);
                            if (jsonString) {
                                if (JSON && JSON.parse) // Use native parsing where available
                                    return JSON.parse(jsonString);
                                return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
                            }
                        }
                        return null;
                    },

                    stringifyJson: function (data, replacer, space) {   // replacer and space are optional
                        if (!JSON || !JSON.stringify)
                            throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
                        return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
                    },

                    postJson: function (urlOrForm, data, options) {
                        options = options || {};
                        var params = options['params'] || {};
                        var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
                        var url = urlOrForm;

                        // If we were given a form, use its 'action' URL and pick out any requested field values
                        if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
                            var originalForm = urlOrForm;
                            url = originalForm.action;
                            for (var i = includeFields.length - 1; i >= 0; i--) {
                                var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
                                for (var j = fields.length - 1; j >= 0; j--)
                                    params[fields[j].name] = fields[j].value;
                            }
                        }

                        data = ko.utils.unwrapObservable(data);
                        var form = document.createElement("form");
                        form.style.display = "none";
                        form.action = url;
                        form.method = "post";
                        for (var key in data) {
                            // Since 'data' this is a model object, we include all properties including those inherited from its prototype
                            var input = document.createElement("input");
                            input.type = "hidden";
                            input.name = key;
                            input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
                            form.appendChild(input);
                        }
                        objectForEach(params, function(key, value) {
                            var input = document.createElement("input");
                            input.type = "hidden";
                            input.name = key;
                            input.value = value;
                            form.appendChild(input);
                        });
                        document.body.appendChild(form);
                        options['submitter'] ? options['submitter'](form) : form.submit();
                        setTimeout(function () { form.parentNode.removeChild(form); }, 0);
                    }
                }
            }());

            ko.exportSymbol('utils', ko.utils);
            ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
            ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
            ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
            ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
            ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
            ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
            ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
            ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
            ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes);
            ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString);
            ko.exportSymbol('utils.extend', ko.utils.extend);
            ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
            ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
            ko.exportSymbol('utils.objectMap', ko.utils.objectMap);
            ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
            ko.exportSymbol('utils.postJson', ko.utils.postJson);
            ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
            ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
            ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
            ko.exportSymbol('utils.range', ko.utils.range);
            ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
            ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
            ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
            ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);
            ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);
            ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);
            ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly

            if (!Function.prototype['bind']) {
                // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
                // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
                Function.prototype['bind'] = function (object) {
                    var originalFunction = this;
                    if (arguments.length === 1) {
                        return function () {
                            return originalFunction.apply(object, arguments);
                        };
                    } else {
                        var partialArgs = Array.prototype.slice.call(arguments, 1);
                        return function () {
                            var args = partialArgs.slice(0);
                            args.push.apply(args, arguments);
                            return originalFunction.apply(object, args);
                        };
                    }
                };
            }

            ko.utils.domData = new (function () {
                var uniqueId = 0;
                var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
                var dataStore = {};

                var getDataForNode, clear;
                if (!ko.utils.ieVersion) {
                    // We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using
                    // it cross-window, so instead we just store the data directly on the node.
                    // See https://github.com/knockout/knockout/issues/2141
                    getDataForNode = function (node, createIfNotFound) {
                        var dataForNode = node[dataStoreKeyExpandoPropertyName];
                        if (!dataForNode && createIfNotFound) {
                            dataForNode = node[dataStoreKeyExpandoPropertyName] = {};
                        }
                        return dataForNode;
                    };
                    clear = function (node) {
                        if (node[dataStoreKeyExpandoPropertyName]) {
                            delete node[dataStoreKeyExpandoPropertyName];
                            return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
                        }
                        return false;
                    };
                } else {
                    // Old IE versions have memory issues if you store objects on the node, so we use a
                    // separate data storage and link to it from the node using a string key.
                    getDataForNode = function (node, createIfNotFound) {
                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
                        var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
                        if (!hasExistingDataStore) {
                            if (!createIfNotFound)
                                return undefined;
                            dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
                            dataStore[dataStoreKey] = {};
                        }
                        return dataStore[dataStoreKey];
                    };
                    clear = function (node) {
                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
                        if (dataStoreKey) {
                            delete dataStore[dataStoreKey];
                            node[dataStoreKeyExpandoPropertyName] = null;
                            return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
                        }
                        return false;
                    };
                }

                return {
                    get: function (node, key) {
                        var dataForNode = getDataForNode(node, false);
                        return dataForNode && dataForNode[key];
                    },
                    set: function (node, key, value) {
                        // Make sure we don't actually create a new domData key if we are actually deleting a value
                        var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */);
                        dataForNode && (dataForNode[key] = value);
                    },
                    getOrSet: function (node, key, value) {
                        var dataForNode = getDataForNode(node, true /* createIfNotFound */);
                        return dataForNode[key] || (dataForNode[key] = value);
                    },
                    clear: clear,

                    nextKey: function () {
                        return (uniqueId++) + dataStoreKeyExpandoPropertyName;
                    }
                };
            })();

            ko.exportSymbol('utils.domData', ko.utils.domData);
            ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully

            ko.utils.domNodeDisposal = new (function () {
                var domDataKey = ko.utils.domData.nextKey();
                var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document
                var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document

                function getDisposeCallbacksCollection(node, createIfNotFound) {
                    var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
                    if ((allDisposeCallbacks === undefined) && createIfNotFound) {
                        allDisposeCallbacks = [];
                        ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
                    }
                    return allDisposeCallbacks;
                }
                function destroyCallbacksCollection(node) {
                    ko.utils.domData.set(node, domDataKey, undefined);
                }

                function cleanSingleNode(node) {
                    // Run all the dispose callbacks
                    var callbacks = getDisposeCallbacksCollection(node, false);
                    if (callbacks) {
                        callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
                        for (var i = 0; i < callbacks.length; i++)
                            callbacks[i](node);
                    }

                    // Erase the DOM data
                    ko.utils.domData.clear(node);

                    // Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
                    ko.utils.domNodeDisposal["cleanExternalData"](node);

                    // Clear any immediate-child comment nodes, as these wouldn't have been found by
                    // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
                    if (cleanableNodeTypesWithDescendants[node.nodeType]) {
                        cleanNodesInList(node.childNodes, true/*onlyComments*/);
                    }
                }

                function cleanNodesInList(nodeList, onlyComments) {
                    var cleanedNodes = [], lastCleanedNode;
                    for (var i = 0; i < nodeList.length; i++) {
                        if (!onlyComments || nodeList[i].nodeType === 8) {
                            cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]);
                            if (nodeList[i] !== lastCleanedNode) {
                                while (i-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1) {}
                            }
                        }
                    }
                }

                return {
                    addDisposeCallback : function(node, callback) {
                        if (typeof callback != "function")
                            throw new Error("Callback must be a function");
                        getDisposeCallbacksCollection(node, true).push(callback);
                    },

                    removeDisposeCallback : function(node, callback) {
                        var callbacksCollection = getDisposeCallbacksCollection(node, false);
                        if (callbacksCollection) {
                            ko.utils.arrayRemoveItem(callbacksCollection, callback);
                            if (callbacksCollection.length == 0)
                                destroyCallbacksCollection(node);
                        }
                    },

                    cleanNode : function(node) {
                        ko.dependencyDetection.ignore(function () {
                            // First clean this node, where applicable
                            if (cleanableNodeTypes[node.nodeType]) {
                                cleanSingleNode(node);

                                // ... then its descendants, where applicable
                                if (cleanableNodeTypesWithDescendants[node.nodeType]) {
                                    cleanNodesInList(node.getElementsByTagName("*"));
                                }
                            }
                        });

                        return node;
                    },

                    removeNode : function(node) {
                        ko.cleanNode(node);
                        if (node.parentNode)
                            node.parentNode.removeChild(node);
                    },

                    "cleanExternalData" : function (node) {
                        // Special support for jQuery here because it's so commonly used.
                        // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
                        // so notify it to tear down any resources associated with the node & descendants here.
                        if (jQueryInstance && (typeof jQueryInstance['cleanData'] == "function"))
                            jQueryInstance['cleanData']([node]);
                    }
                };
            })();
            ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
            ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
            ko.exportSymbol('cleanNode', ko.cleanNode);
            ko.exportSymbol('removeNode', ko.removeNode);
            ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
            ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
            ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
            (function () {
                var none = [0, "", ""],
                    table = [1, "<table>", "</table>"],
                    tbody = [2, "<table><tbody>", "</tbody></table>"],
                    tr = [3, "<table><tbody><tr>", "</tr></tbody></table>"],
                    select = [1, "<select multiple='multiple'>", "</select>"],
                    lookup = {
                        'thead': table,
                        'tbody': table,
                        'tfoot': table,
                        'tr': tbody,
                        'td': tr,
                        'th': tr,
                        'option': select,
                        'optgroup': select
                    },

                    // This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.
                    mayRequireCreateElementHack = ko.utils.ieVersion <= 8;

                function getWrap(tags) {
                    var m = tags.match(/^(?:<!--.*?-->\s*?)*?<([a-z]+)[\s>]/);
                    return (m && lookup[m[1]]) || none;
                }

                function simpleHtmlParse(html, documentContext) {
                    documentContext || (documentContext = document);
                    var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;

                    // Based on jQuery's "clean" function, but only accounting for table-related elements.
                    // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly

                    // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
                    // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
                    // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
                    // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.

                    // Trim whitespace, otherwise indexOf won't work as expected
                    var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
                        wrap = getWrap(tags),
                        depth = wrap[0];

                    // Go to html and back, then peel off extra wrappers
                    // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
                    var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
                    if (typeof windowContext['innerShiv'] == "function") {
                        // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
                        // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
                        // somehow shims the native APIs so it just works anyway)
                        div.appendChild(windowContext['innerShiv'](markup));
                    } else {
                        if (mayRequireCreateElementHack) {
                            // The document.createElement('my-element') trick to enable custom elements in IE6-8
                            // only works if we assign innerHTML on an element associated with that document.
                            documentContext.body.appendChild(div);
                        }

                        div.innerHTML = markup;

                        if (mayRequireCreateElementHack) {
                            div.parentNode.removeChild(div);
                        }
                    }

                    // Move to the right depth
                    while (depth--)
                        div = div.lastChild;

                    return ko.utils.makeArray(div.lastChild.childNodes);
                }

                function jQueryHtmlParse(html, documentContext) {
                    // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
                    if (jQueryInstance['parseHTML']) {
                        return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null
                    } else {
                        // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
                        var elems = jQueryInstance['clean']([html], documentContext);

                        // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
                        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
                        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
                        if (elems && elems[0]) {
                            // Find the top-most parent element that's a direct child of a document fragment
                            var elem = elems[0];
                            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
                                elem = elem.parentNode;
                            // ... then detach it
                            if (elem.parentNode)
                                elem.parentNode.removeChild(elem);
                        }

                        return elems;
                    }
                }

                ko.utils.parseHtmlFragment = function(html, documentContext) {
                    return jQueryInstance ?
                        jQueryHtmlParse(html, documentContext) :   // As below, benefit from jQuery's optimisations where possible
                        simpleHtmlParse(html, documentContext);  // ... otherwise, this simple logic will do in most common cases.
                };

                ko.utils.parseHtmlForTemplateNodes = function(html, documentContext) {
                    var nodes = ko.utils.parseHtmlFragment(html, documentContext);
                    return (nodes.length && nodes[0].parentElement) || ko.utils.moveCleanedNodesToContainerElement(nodes);
                };

                ko.utils.setHtml = function(node, html) {
                    ko.utils.emptyDomNode(node);

                    // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
                    html = ko.utils.unwrapObservable(html);

                    if ((html !== null) && (html !== undefined)) {
                        if (typeof html != 'string')
                            html = html.toString();

                        // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
                        // for example <tr> elements which are not normally allowed to exist on their own.
                        // If you've referenced jQuery we'll use that rather than duplicating its code.
                        if (jQueryInstance) {
                            jQueryInstance(node)['html'](html);
                        } else {
                            // ... otherwise, use KO's own parsing logic.
                            var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
                            for (var i = 0; i < parsedNodes.length; i++)
                                node.appendChild(parsedNodes[i]);
                        }
                    }
                };
            })();

            ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
            ko.exportSymbol('utils.setHtml', ko.utils.setHtml);

            ko.memoization = (function () {
                var memos = {};

                function randomMax8HexChars() {
                    return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
                }
                function generateRandomId() {
                    return randomMax8HexChars() + randomMax8HexChars();
                }
                function findMemoNodes(rootNode, appendToArray) {
                    if (!rootNode)
                        return;
                    if (rootNode.nodeType == 8) {
                        var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
                        if (memoId != null)
                            appendToArray.push({ domNode: rootNode, memoId: memoId });
                    } else if (rootNode.nodeType == 1) {
                        for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
                            findMemoNodes(childNodes[i], appendToArray);
                    }
                }

                return {
                    memoize: function (callback) {
                        if (typeof callback != "function")
                            throw new Error("You can only pass a function to ko.memoization.memoize()");
                        var memoId = generateRandomId();
                        memos[memoId] = callback;
                        return "<!--[ko_memo:" + memoId + "]-->";
                    },

                    unmemoize: function (memoId, callbackParams) {
                        var callback = memos[memoId];
                        if (callback === undefined)
                            throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
                        try {
                            callback.apply(null, callbackParams || []);
                            return true;
                        }
                        finally { delete memos[memoId]; }
                    },

                    unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
                        var memos = [];
                        findMemoNodes(domNode, memos);
                        for (var i = 0, j = memos.length; i < j; i++) {
                            var node = memos[i].domNode;
                            var combinedParams = [node];
                            if (extraCallbackParamsArray)
                                ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
                            ko.memoization.unmemoize(memos[i].memoId, combinedParams);
                            node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
                            if (node.parentNode)
                                node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
                        }
                    },

                    parseMemoText: function (memoText) {
                        var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
                        return match ? match[1] : null;
                    }
                };
            })();

            ko.exportSymbol('memoization', ko.memoization);
            ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
            ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
            ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
            ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
            ko.tasks = (function () {
                var scheduler,
                    taskQueue = [],
                    taskQueueLength = 0,
                    nextHandle = 1,
                    nextIndexToProcess = 0;

                if (window['MutationObserver']) {
                    // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
                    // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
                    scheduler = (function (callback) {
                        var div = document.createElement("div");
                        new MutationObserver(callback).observe(div, {attributes: true});
                        return function () { div.classList.toggle("foo"); };
                    })(scheduledProcess);
                } else if (document && "onreadystatechange" in document.createElement("script")) {
                    // IE 6-10
                    // From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
                    scheduler = function (callback) {
                        var script = document.createElement("script");
                        script.onreadystatechange = function () {
                            script.onreadystatechange = null;
                            document.documentElement.removeChild(script);
                            script = null;
                            callback();
                        };
                        document.documentElement.appendChild(script);
                    };
                } else {
                    scheduler = function (callback) {
                        setTimeout(callback, 0);
                    };
                }

                function processTasks() {
                    if (taskQueueLength) {
                        // Each mark represents the end of a logical group of tasks and the number of these groups is
                        // limited to prevent unchecked recursion.
                        var mark = taskQueueLength, countMarks = 0;

                        // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
                        for (var task; nextIndexToProcess < taskQueueLength; ) {
                            if (task = taskQueue[nextIndexToProcess++]) {
                                if (nextIndexToProcess > mark) {
                                    if (++countMarks >= 5000) {
                                        nextIndexToProcess = taskQueueLength;   // skip all tasks remaining in the queue since any of them could be causing the recursion
                                        ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
                                        break;
                                    }
                                    mark = taskQueueLength;
                                }
                                try {
                                    task();
                                } catch (ex) {
                                    ko.utils.deferError(ex);
                                }
                            }
                        }
                    }
                }

                function scheduledProcess() {
                    processTasks();

                    // Reset the queue
                    nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
                }

                function scheduleTaskProcessing() {
                    ko.tasks['scheduler'](scheduledProcess);
                }

                var tasks = {
                    'scheduler': scheduler,     // Allow overriding the scheduler

                    schedule: function (func) {
                        if (!taskQueueLength) {
                            scheduleTaskProcessing();
                        }

                        taskQueue[taskQueueLength++] = func;
                        return nextHandle++;
                    },

                    cancel: function (handle) {
                        var index = handle - (nextHandle - taskQueueLength);
                        if (index >= nextIndexToProcess && index < taskQueueLength) {
                            taskQueue[index] = null;
                        }
                    },

                    // For testing only: reset the queue and return the previous queue length
                    'resetForTesting': function () {
                        var length = taskQueueLength - nextIndexToProcess;
                        nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
                        return length;
                    },

                    runEarly: processTasks
                };

                return tasks;
            })();

            ko.exportSymbol('tasks', ko.tasks);
            ko.exportSymbol('tasks.schedule', ko.tasks.schedule);
//ko.exportSymbol('tasks.cancel', ko.tasks.cancel);  "cancel" isn't minified
            ko.exportSymbol('tasks.runEarly', ko.tasks.runEarly);
            ko.extenders = {
                'throttle': function(target, timeout) {
                    // Throttling means two things:

                    // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
                    //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
                    target['throttleEvaluation'] = timeout;

                    // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
                    //     so the target cannot change value synchronously or faster than a certain rate
                    var writeTimeoutInstance = null;
                    return ko.dependentObservable({
                        'read': target,
                        'write': function(value) {
                            clearTimeout(writeTimeoutInstance);
                            writeTimeoutInstance = ko.utils.setTimeout(function() {
                                target(value);
                            }, timeout);
                        }
                    });
                },

                'rateLimit': function(target, options) {
                    var timeout, method, limitFunction;

                    if (typeof options == 'number') {
                        timeout = options;
                    } else {
                        timeout = options['timeout'];
                        method = options['method'];
                    }

                    // rateLimit supersedes deferred updates
                    target._deferUpdates = false;

                    limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ?  debounce : throttle;
                    target.limit(function(callback) {
                        return limitFunction(callback, timeout, options);
                    });
                },

                'deferred': function(target, options) {
                    if (options !== true) {
                        throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.')
                    }

                    if (!target._deferUpdates) {
                        target._deferUpdates = true;
                        target.limit(function (callback) {
                            var handle,
                                ignoreUpdates = false;
                            return function () {
                                if (!ignoreUpdates) {
                                    ko.tasks.cancel(handle);
                                    handle = ko.tasks.schedule(callback);

                                    try {
                                        ignoreUpdates = true;
                                        target['notifySubscribers'](undefined, 'dirty');
                                    } finally {
                                        ignoreUpdates = false;
                                    }
                                }
                            };
                        });
                    }
                },

                'notify': function(target, notifyWhen) {
                    target["equalityComparer"] = notifyWhen == "always" ?
                        null :  // null equalityComparer means to always notify
                        valuesArePrimitiveAndEqual;
                }
            };

            var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };
            function valuesArePrimitiveAndEqual(a, b) {
                var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
                return oldValueIsPrimitive ? (a === b) : false;
            }

            function throttle(callback, timeout) {
                var timeoutInstance;
                return function () {
                    if (!timeoutInstance) {
                        timeoutInstance = ko.utils.setTimeout(function () {
                            timeoutInstance = undefined;
                            callback();
                        }, timeout);
                    }
                };
            }

            function debounce(callback, timeout) {
                var timeoutInstance;
                return function () {
                    clearTimeout(timeoutInstance);
                    timeoutInstance = ko.utils.setTimeout(callback, timeout);
                };
            }

            function applyExtenders(requestedExtenders) {
                var target = this;
                if (requestedExtenders) {
                    ko.utils.objectForEach(requestedExtenders, function(key, value) {
                        var extenderHandler = ko.extenders[key];
                        if (typeof extenderHandler == 'function') {
                            target = extenderHandler(target, value) || target;
                        }
                    });
                }
                return target;
            }

            ko.exportSymbol('extenders', ko.extenders);

            ko.subscription = function (target, callback, disposeCallback) {
                this._target = target;
                this._callback = callback;
                this._disposeCallback = disposeCallback;
                this._isDisposed = false;
                this._node = null;
                this._domNodeDisposalCallback = null;
                ko.exportProperty(this, 'dispose', this.dispose);
                ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved);
            };
            ko.subscription.prototype.dispose = function () {
                var self = this;
                if (!self._isDisposed) {
                    if (self._domNodeDisposalCallback) {
                        ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
                    }
                    self._isDisposed = true;
                    self._disposeCallback();

                    self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null;
                }
            };
            ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) {
                this._node = node;
                ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));
            };

            ko.subscribable = function () {
                ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
                ko_subscribable_fn.init(this);
            }

            var defaultEvent = "change";

// Moved out of "limit" to avoid the extra closure
            function limitNotifySubscribers(value, event) {
                if (!event || event === defaultEvent) {
                    this._limitChange(value);
                } else if (event === 'beforeChange') {
                    this._limitBeforeChange(value);
                } else {
                    this._origNotifySubscribers(value, event);
                }
            }

            var ko_subscribable_fn = {
                init: function(instance) {
                    instance._subscriptions = { "change": [] };
                    instance._versionNumber = 1;
                },

                subscribe: function (callback, callbackTarget, event) {
                    var self = this;

                    event = event || defaultEvent;
                    var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;

                    var subscription = new ko.subscription(self, boundCallback, function () {
                        ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
                        if (self.afterSubscriptionRemove)
                            self.afterSubscriptionRemove(event);
                    });

                    if (self.beforeSubscriptionAdd)
                        self.beforeSubscriptionAdd(event);

                    if (!self._subscriptions[event])
                        self._subscriptions[event] = [];
                    self._subscriptions[event].push(subscription);

                    return subscription;
                },

                "notifySubscribers": function (valueToNotify, event) {
                    event = event || defaultEvent;
                    if (event === defaultEvent) {
                        this.updateVersion();
                    }
                    if (this.hasSubscriptionsForEvent(event)) {
                        var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
                        try {
                            ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
                            for (var i = 0, subscription; subscription = subs[i]; ++i) {
                                // In case a subscription was disposed during the arrayForEach cycle, check
                                // for isDisposed on each subscription before invoking its callback
                                if (!subscription._isDisposed)
                                    subscription._callback(valueToNotify);
                            }
                        } finally {
                            ko.dependencyDetection.end(); // End suppressing dependency detection
                        }
                    }
                },

                getVersion: function () {
                    return this._versionNumber;
                },

                hasChanged: function (versionToCheck) {
                    return this.getVersion() !== versionToCheck;
                },

                updateVersion: function () {
                    ++this._versionNumber;
                },

                limit: function(limitFunction) {
                    var self = this, selfIsObservable = ko.isObservable(self),
                        ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
                        beforeChange = 'beforeChange';

                    if (!self._origNotifySubscribers) {
                        self._origNotifySubscribers = self["notifySubscribers"];
                        self["notifySubscribers"] = limitNotifySubscribers;
                    }

                    var finish = limitFunction(function() {
                        self._notificationIsPending = false;

                        // If an observable provided a reference to itself, access it to get the latest value.
                        // This allows computed observables to delay calculating their value until needed.
                        if (selfIsObservable && pendingValue === self) {
                            pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
                        }
                        var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));

                        didUpdate = notifyNextChange = ignoreBeforeChange = false;

                        if (shouldNotify) {
                            self._origNotifySubscribers(previousValue = pendingValue);
                        }
                    });

                    self._limitChange = function(value, isDirty) {
                        if (!isDirty || !self._notificationIsPending) {
                            didUpdate = !isDirty;
                        }
                        self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
                        self._notificationIsPending = ignoreBeforeChange = true;
                        pendingValue = value;
                        finish();
                    };
                    self._limitBeforeChange = function(value) {
                        if (!ignoreBeforeChange) {
                            previousValue = value;
                            self._origNotifySubscribers(value, beforeChange);
                        }
                    };
                    self._recordUpdate = function() {
                        didUpdate = true;
                    };
                    self._notifyNextChangeIfValueIsDifferent = function() {
                        if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
                            notifyNextChange = true;
                        }
                    };
                },

                hasSubscriptionsForEvent: function(event) {
                    return this._subscriptions[event] && this._subscriptions[event].length;
                },

                getSubscriptionsCount: function (event) {
                    if (event) {
                        return this._subscriptions[event] && this._subscriptions[event].length || 0;
                    } else {
                        var total = 0;
                        ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
                            if (eventName !== 'dirty')
                                total += subscriptions.length;
                        });
                        return total;
                    }
                },

                isDifferent: function(oldValue, newValue) {
                    return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
                },

                toString: function() {
                    return '[object Object]'
                },

                extend: applyExtenders
            };

            ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);
            ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);
            ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);
            ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);

// For browsers that support proto assignment, we overwrite the prototype of each
// observable instance. Since observables are functions, we need Function.prototype
// to still be in the prototype chain.
            if (ko.utils.canSetPrototype) {
                ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);
            }

            ko.subscribable['fn'] = ko_subscribable_fn;


            ko.isSubscribable = function (instance) {
                return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
            };

            ko.exportSymbol('subscribable', ko.subscribable);
            ko.exportSymbol('isSubscribable', ko.isSubscribable);

            ko.computedContext = ko.dependencyDetection = (function () {
                var outerFrames = [],
                    currentFrame,
                    lastId = 0;

                // Return a unique ID that can be assigned to an observable for dependency tracking.
                // Theoretically, you could eventually overflow the number storage size, resulting
                // in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
                // or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
                // take over 285 years to reach that number.
                // Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
                function getId() {
                    return ++lastId;
                }

                function begin(options) {
                    outerFrames.push(currentFrame);
                    currentFrame = options;
                }

                function end() {
                    currentFrame = outerFrames.pop();
                }

                return {
                    begin: begin,

                    end: end,

                    registerDependency: function (subscribable) {
                        if (currentFrame) {
                            if (!ko.isSubscribable(subscribable))
                                throw new Error("Only subscribable things can act as dependencies");
                            currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
                        }
                    },

                    ignore: function (callback, callbackTarget, callbackArgs) {
                        try {
                            begin();
                            return callback.apply(callbackTarget, callbackArgs || []);
                        } finally {
                            end();
                        }
                    },

                    getDependenciesCount: function () {
                        if (currentFrame)
                            return currentFrame.computed.getDependenciesCount();
                    },

                    getDependencies: function () {
                        if (currentFrame)
                            return currentFrame.computed.getDependencies();
                    },

                    isInitial: function() {
                        if (currentFrame)
                            return currentFrame.isInitial;
                    },

                    computed: function() {
                        if (currentFrame)
                            return currentFrame.computed;
                    }
                };
            })();

            ko.exportSymbol('computedContext', ko.computedContext);
            ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
            ko.exportSymbol('computedContext.getDependencies', ko.computedContext.getDependencies);
            ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
            ko.exportSymbol('computedContext.registerDependency', ko.computedContext.registerDependency);

            ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
            var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');

            ko.observable = function (initialValue) {
                function observable() {
                    if (arguments.length > 0) {
                        // Write

                        // Ignore writes if the value hasn't changed
                        if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
                            observable.valueWillMutate();
                            observable[observableLatestValue] = arguments[0];
                            observable.valueHasMutated();
                        }
                        return this; // Permits chained assignments
                    }
                    else {
                        // Read
                        ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
                        return observable[observableLatestValue];
                    }
                }

                observable[observableLatestValue] = initialValue;

                // Inherit from 'subscribable'
                if (!ko.utils.canSetPrototype) {
                    // 'subscribable' won't be on the prototype chain unless we put it there directly
                    ko.utils.extend(observable, ko.subscribable['fn']);
                }
                ko.subscribable['fn'].init(observable);

                // Inherit from 'observable'
                ko.utils.setPrototypeOfOrExtend(observable, observableFn);

                if (ko.options['deferUpdates']) {
                    ko.extenders['deferred'](observable, true);
                }

                return observable;
            }

// Define prototype for observables
            var observableFn = {
                'equalityComparer': valuesArePrimitiveAndEqual,
                peek: function() { return this[observableLatestValue]; },
                valueHasMutated: function () {
                    this['notifySubscribers'](this[observableLatestValue], 'spectate');
                    this['notifySubscribers'](this[observableLatestValue]);
                },
                valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
            };

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observable constructor
            if (ko.utils.canSetPrototype) {
                ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
            }

            var protoProperty = ko.observable.protoProperty = '__ko_proto__';
            observableFn[protoProperty] = ko.observable;

            ko.isObservable = function (instance) {
                var proto = typeof instance == 'function' && instance[protoProperty];
                if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {
                    throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
                }
                return !!proto;
            };

            ko.isWriteableObservable = function (instance) {
                return (typeof instance == 'function' && (
                    (instance[protoProperty] === observableFn[protoProperty]) ||  // Observable
                    (instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction)));   // Writable computed observable
            };

            ko.exportSymbol('observable', ko.observable);
            ko.exportSymbol('isObservable', ko.isObservable);
            ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
            ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);
            ko.exportSymbol('observable.fn', observableFn);
            ko.exportProperty(observableFn, 'peek', observableFn.peek);
            ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);
            ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);
            ko.observableArray = function (initialValues) {
                initialValues = initialValues || [];

                if (typeof initialValues != 'object' || !('length' in initialValues))
                    throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");

                var result = ko.observable(initialValues);
                ko.utils.setPrototypeOfOrExtend(result, ko.observableArray['fn']);
                return result.extend({'trackArrayChanges':true});
            };

            ko.observableArray['fn'] = {
                'remove': function (valueOrPredicate) {
                    var underlyingArray = this.peek();
                    var removedValues = [];
                    var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
                    for (var i = 0; i < underlyingArray.length; i++) {
                        var value = underlyingArray[i];
                        if (predicate(value)) {
                            if (removedValues.length === 0) {
                                this.valueWillMutate();
                            }
                            if (underlyingArray[i] !== value) {
                                throw Error("Array modified during remove; cannot remove item");
                            }
                            removedValues.push(value);
                            underlyingArray.splice(i, 1);
                            i--;
                        }
                    }
                    if (removedValues.length) {
                        this.valueHasMutated();
                    }
                    return removedValues;
                },

                'removeAll': function (arrayOfValues) {
                    // If you passed zero args, we remove everything
                    if (arrayOfValues === undefined) {
                        var underlyingArray = this.peek();
                        var allValues = underlyingArray.slice(0);
                        this.valueWillMutate();
                        underlyingArray.splice(0, underlyingArray.length);
                        this.valueHasMutated();
                        return allValues;
                    }
                    // If you passed an arg, we interpret it as an array of entries to remove
                    if (!arrayOfValues)
                        return [];
                    return this['remove'](function (value) {
                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
                    });
                },

                'destroy': function (valueOrPredicate) {
                    var underlyingArray = this.peek();
                    var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
                    this.valueWillMutate();
                    for (var i = underlyingArray.length - 1; i >= 0; i--) {
                        var value = underlyingArray[i];
                        if (predicate(value))
                            value["_destroy"] = true;
                    }
                    this.valueHasMutated();
                },

                'destroyAll': function (arrayOfValues) {
                    // If you passed zero args, we destroy everything
                    if (arrayOfValues === undefined)
                        return this['destroy'](function() { return true });

                    // If you passed an arg, we interpret it as an array of entries to destroy
                    if (!arrayOfValues)
                        return [];
                    return this['destroy'](function (value) {
                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
                    });
                },

                'indexOf': function (item) {
                    var underlyingArray = this();
                    return ko.utils.arrayIndexOf(underlyingArray, item);
                },

                'replace': function(oldItem, newItem) {
                    var index = this['indexOf'](oldItem);
                    if (index >= 0) {
                        this.valueWillMutate();
                        this.peek()[index] = newItem;
                        this.valueHasMutated();
                    }
                },

                'sorted': function (compareFunction) {
                    var arrayCopy = this().slice(0);
                    return compareFunction ? arrayCopy.sort(compareFunction) : arrayCopy.sort();
                },

                'reversed': function () {
                    return this().slice(0).reverse();
                }
            };

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observableArray constructor
            if (ko.utils.canSetPrototype) {
                ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
            }

// Populate ko.observableArray.fn with read/write functions from native arrays
// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
            ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
                ko.observableArray['fn'][methodName] = function () {
                    // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
                    // (for consistency with mutating regular observables)
                    var underlyingArray = this.peek();
                    this.valueWillMutate();
                    this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
                    var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
                    this.valueHasMutated();
                    // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
                    return methodCallResult === underlyingArray ? this : methodCallResult;
                };
            });

// Populate ko.observableArray.fn with read-only functions from native arrays
            ko.utils.arrayForEach(["slice"], function (methodName) {
                ko.observableArray['fn'][methodName] = function () {
                    var underlyingArray = this();
                    return underlyingArray[methodName].apply(underlyingArray, arguments);
                };
            });

            ko.isObservableArray = function (instance) {
                return ko.isObservable(instance)
                    && typeof instance["remove"] == "function"
                    && typeof instance["push"] == "function";
            };

            ko.exportSymbol('observableArray', ko.observableArray);
            ko.exportSymbol('isObservableArray', ko.isObservableArray);
            var arrayChangeEventName = 'arrayChange';
            ko.extenders['trackArrayChanges'] = function(target, options) {
                // Use the provided options--each call to trackArrayChanges overwrites the previously set options
                target.compareArrayOptions = {};
                if (options && typeof options == "object") {
                    ko.utils.extend(target.compareArrayOptions, options);
                }
                target.compareArrayOptions['sparse'] = true;

                // Only modify the target observable once
                if (target.cacheDiffForKnownOperation) {
                    return;
                }
                var trackingChanges = false,
                    cachedDiff = null,
                    changeSubscription,
                    spectateSubscription,
                    pendingChanges = 0,
                    previousContents,
                    underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
                    underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;

                // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
                target.beforeSubscriptionAdd = function (event) {
                    if (underlyingBeforeSubscriptionAddFunction) {
                        underlyingBeforeSubscriptionAddFunction.call(target, event);
                    }
                    if (event === arrayChangeEventName) {
                        trackChanges();
                    }
                };
                // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
                target.afterSubscriptionRemove = function (event) {
                    if (underlyingAfterSubscriptionRemoveFunction) {
                        underlyingAfterSubscriptionRemoveFunction.call(target, event);
                    }
                    if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
                        if (changeSubscription) {
                            changeSubscription.dispose();
                        }
                        if (spectateSubscription) {
                            spectateSubscription.dispose();
                        }
                        spectateSubscription = changeSubscription = null;
                        trackingChanges = false;
                        previousContents = undefined;
                    }
                };

                function trackChanges() {
                    if (trackingChanges) {
                        // Whenever there's a new subscription and there are pending notifications, make sure all previous
                        // subscriptions are notified of the change so that all subscriptions are in sync.
                        notifyChanges();
                        return;
                    }

                    trackingChanges = true;

                    // Track how many times the array actually changed value
                    spectateSubscription = target.subscribe(function () {
                        ++pendingChanges;
                    }, null, "spectate");

                    // Each time the array changes value, capture a clone so that on the next
                    // change it's possible to produce a diff
                    previousContents = [].concat(target.peek() || []);
                    cachedDiff = null;
                    changeSubscription = target.subscribe(notifyChanges);

                    function notifyChanges() {
                        if (pendingChanges) {
                            // Make a copy of the current contents and ensure it's an array
                            var currentContents = [].concat(target.peek() || []), changes;

                            // Compute the diff and issue notifications, but only if someone is listening
                            if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
                                changes = getChanges(previousContents, currentContents);
                            }

                            // Eliminate references to the old, removed items, so they can be GCed
                            previousContents = currentContents;
                            cachedDiff = null;
                            pendingChanges = 0;

                            if (changes && changes.length) {
                                target['notifySubscribers'](changes, arrayChangeEventName);
                            }
                        }
                    }
                }

                function getChanges(previousContents, currentContents) {
                    // We try to re-use cached diffs.
                    // The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates,
                    // which without this check would not be compatible with arrayChange notifications. Normally,
                    // notifications are issued immediately so we wouldn't be queueing up more than one.
                    if (!cachedDiff || pendingChanges > 1) {
                        cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);
                    }

                    return cachedDiff;
                }

                target.cacheDiffForKnownOperation = function(rawArray, operationName, args) {
                    // Only run if we're currently tracking changes for this observable array
                    // and there aren't any pending deferred notifications.
                    if (!trackingChanges || pendingChanges) {
                        return;
                    }
                    var diff = [],
                        arrayLength = rawArray.length,
                        argsLength = args.length,
                        offset = 0;

                    function pushDiff(status, value, index) {
                        return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
                    }
                    switch (operationName) {
                        case 'push':
                            offset = arrayLength;
                        case 'unshift':
                            for (var index = 0; index < argsLength; index++) {
                                pushDiff('added', args[index], offset + index);
                            }
                            break;

                        case 'pop':
                            offset = arrayLength - 1;
                        case 'shift':
                            if (arrayLength) {
                                pushDiff('deleted', rawArray[offset], offset);
                            }
                            break;

                        case 'splice':
                            // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
                            // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
                            var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),
                                endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength),
                                endAddIndex = startIndex + argsLength - 2,
                                endIndex = Math.max(endDeleteIndex, endAddIndex),
                                additions = [], deletions = [];
                            for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
                                if (index < endDeleteIndex)
                                    deletions.push(pushDiff('deleted', rawArray[index], index));
                                if (index < endAddIndex)
                                    additions.push(pushDiff('added', args[argsIndex], index));
                            }
                            ko.utils.findMovesInArrayComparison(deletions, additions);
                            break;

                        default:
                            return;
                    }
                    cachedDiff = diff;
                };
            };
            var computedState = ko.utils.createSymbolOrString('_state');

            ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
                if (typeof evaluatorFunctionOrOptions === "object") {
                    // Single-parameter syntax - everything is on this "options" param
                    options = evaluatorFunctionOrOptions;
                } else {
                    // Multi-parameter syntax - construct the options according to the params passed
                    options = options || {};
                    if (evaluatorFunctionOrOptions) {
                        options["read"] = evaluatorFunctionOrOptions;
                    }
                }
                if (typeof options["read"] != "function")
                    throw Error("Pass a function that returns the value of the ko.computed");

                var writeFunction = options["write"];
                var state = {
                    latestValue: undefined,
                    isStale: true,
                    isDirty: true,
                    isBeingEvaluated: false,
                    suppressDisposalUntilDisposeWhenReturnsFalse: false,
                    isDisposed: false,
                    pure: false,
                    isSleeping: false,
                    readFunction: options["read"],
                    evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
                    disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
                    disposeWhen: options["disposeWhen"] || options.disposeWhen,
                    domNodeDisposalCallback: null,
                    dependencyTracking: {},
                    dependenciesCount: 0,
                    evaluationTimeoutInstance: null
                };

                function computedObservable() {
                    if (arguments.length > 0) {
                        if (typeof writeFunction === "function") {
                            // Writing a value
                            writeFunction.apply(state.evaluatorFunctionTarget, arguments);
                        } else {
                            throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
                        }
                        return this; // Permits chained assignments
                    } else {
                        // Reading the value
                        if (!state.isDisposed) {
                            ko.dependencyDetection.registerDependency(computedObservable);
                        }
                        if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) {
                            computedObservable.evaluateImmediate();
                        }
                        return state.latestValue;
                    }
                }

                computedObservable[computedState] = state;
                computedObservable.hasWriteFunction = typeof writeFunction === "function";

                // Inherit from 'subscribable'
                if (!ko.utils.canSetPrototype) {
                    // 'subscribable' won't be on the prototype chain unless we put it there directly
                    ko.utils.extend(computedObservable, ko.subscribable['fn']);
                }
                ko.subscribable['fn'].init(computedObservable);

                // Inherit from 'computed'
                ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);

                if (options['pure']) {
                    state.pure = true;
                    state.isSleeping = true;     // Starts off sleeping; will awake on the first subscription
                    ko.utils.extend(computedObservable, pureComputedOverrides);
                } else if (options['deferEvaluation']) {
                    ko.utils.extend(computedObservable, deferEvaluationOverrides);
                }

                if (ko.options['deferUpdates']) {
                    ko.extenders['deferred'](computedObservable, true);
                }

                if (DEBUG) {
                    // #1731 - Aid debugging by exposing the computed's options
                    computedObservable["_options"] = options;
                }

                if (state.disposeWhenNodeIsRemoved) {
                    // Since this computed is associated with a DOM node, and we don't want to dispose the computed
                    // until the DOM node is *removed* from the document (as opposed to never having been in the document),
                    // we'll prevent disposal until "disposeWhen" first returns false.
                    state.suppressDisposalUntilDisposeWhenReturnsFalse = true;

                    // disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result"
                    // behaviour even if there's no specific node to watch. In that case, clear the option so we don't try
                    // to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't
                    // be documented or used by application code, as it's likely to change in a future version of KO.
                    if (!state.disposeWhenNodeIsRemoved.nodeType) {
                        state.disposeWhenNodeIsRemoved = null;
                    }
                }

                // Evaluate, unless sleeping or deferEvaluation is true
                if (!state.isSleeping && !options['deferEvaluation']) {
                    computedObservable.evaluateImmediate();
                }

                // Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
                // removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
                if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
                    ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
                        computedObservable.dispose();
                    });
                }

                return computedObservable;
            };

// Utility function that disposes a given dependencyTracking entry
            function computedDisposeDependencyCallback(id, entryToDispose) {
                if (entryToDispose !== null && entryToDispose.dispose) {
                    entryToDispose.dispose();
                }
            }

// This function gets called each time a dependency is detected while evaluating a computed.
// It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.
            function computedBeginDependencyDetectionCallback(subscribable, id) {
                var computedObservable = this.computedObservable,
                    state = computedObservable[computedState];
                if (!state.isDisposed) {
                    if (this.disposalCount && this.disposalCandidates[id]) {
                        // Don't want to dispose this subscription, as it's still being used
                        computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);
                        this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway
                        --this.disposalCount;
                    } else if (!state.dependencyTracking[id]) {
                        // Brand new subscription - add it
                        computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));
                    }
                    // If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks)
                    if (subscribable._notificationIsPending) {
                        subscribable._notifyNextChangeIfValueIsDifferent();
                    }
                }
            }

            var computedFn = {
                "equalityComparer": valuesArePrimitiveAndEqual,
                getDependenciesCount: function () {
                    return this[computedState].dependenciesCount;
                },
                getDependencies: function () {
                    var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];

                    ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
                        dependentObservables[dependency._order] = dependency._target;
                    });

                    return dependentObservables;
                },
                hasAncestorDependency: function (obs) {
                    if (!this[computedState].dependenciesCount) {
                        return false;
                    }
                    var dependencies = this.getDependencies();
                    if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {
                        return true;
                    }
                    return !!ko.utils.arrayFirst(dependencies, function (dep) {
                        return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);
                    });
                },
                addDependencyTracking: function (id, target, trackingObj) {
                    if (this[computedState].pure && target === this) {
                        throw Error("A 'pure' computed must not be called recursively");
                    }

                    this[computedState].dependencyTracking[id] = trackingObj;
                    trackingObj._order = this[computedState].dependenciesCount++;
                    trackingObj._version = target.getVersion();
                },
                haveDependenciesChanged: function () {
                    var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
                    for (id in dependencyTracking) {
                        if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {
                            dependency = dependencyTracking[id];
                            if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) {
                                return true;
                            }
                        }
                    }
                },
                markDirty: function () {
                    // Process "dirty" events if we can handle delayed notifications
                    if (this._evalDelayed && !this[computedState].isBeingEvaluated) {
                        this._evalDelayed(false /*isChange*/);
                    }
                },
                isActive: function () {
                    var state = this[computedState];
                    return state.isDirty || state.dependenciesCount > 0;
                },
                respondToChange: function () {
                    // Ignore "change" events if we've already scheduled a delayed notification
                    if (!this._notificationIsPending) {
                        this.evaluatePossiblyAsync();
                    } else if (this[computedState].isDirty) {
                        this[computedState].isStale = true;
                    }
                },
                subscribeToDependency: function (target) {
                    if (target._deferUpdates) {
                        var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),
                            changeSub = target.subscribe(this.respondToChange, this);
                        return {
                            _target: target,
                            dispose: function () {
                                dirtySub.dispose();
                                changeSub.dispose();
                            }
                        };
                    } else {
                        return target.subscribe(this.evaluatePossiblyAsync, this);
                    }
                },
                evaluatePossiblyAsync: function () {
                    var computedObservable = this,
                        throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
                    if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
                        clearTimeout(this[computedState].evaluationTimeoutInstance);
                        this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
                            computedObservable.evaluateImmediate(true /*notifyChange*/);
                        }, throttleEvaluationTimeout);
                    } else if (computedObservable._evalDelayed) {
                        computedObservable._evalDelayed(true /*isChange*/);
                    } else {
                        computedObservable.evaluateImmediate(true /*notifyChange*/);
                    }
                },
                evaluateImmediate: function (notifyChange) {
                    var computedObservable = this,
                        state = computedObservable[computedState],
                        disposeWhen = state.disposeWhen,
                        changed = false;

                    if (state.isBeingEvaluated) {
                        // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
                        // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
                        // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
                        // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
                        return;
                    }

                    // Do not evaluate (and possibly capture new dependencies) if disposed
                    if (state.isDisposed) {
                        return;
                    }

                    if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {
                        // See comment above about suppressDisposalUntilDisposeWhenReturnsFalse
                        if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {
                            computedObservable.dispose();
                            return;
                        }
                    } else {
                        // It just did return false, so we can stop suppressing now
                        state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
                    }

                    state.isBeingEvaluated = true;
                    try {
                        changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
                    } finally {
                        state.isBeingEvaluated = false;
                    }

                    return changed;
                },
                evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
                    // This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.
                    // Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,
                    // which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).

                    var computedObservable = this,
                        state = computedObservable[computedState],
                        changed = false;

                    // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
                    // Then, during evaluation, we cross off any that are in fact still being used.
                    var isInitial = state.pure ? undefined : !state.dependenciesCount,   // If we're evaluating when there are no previous dependencies, it must be the first time
                        dependencyDetectionContext = {
                            computedObservable: computedObservable,
                            disposalCandidates: state.dependencyTracking,
                            disposalCount: state.dependenciesCount
                        };

                    ko.dependencyDetection.begin({
                        callbackTarget: dependencyDetectionContext,
                        callback: computedBeginDependencyDetectionCallback,
                        computed: computedObservable,
                        isInitial: isInitial
                    });

                    state.dependencyTracking = {};
                    state.dependenciesCount = 0;

                    var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);

                    if (!state.dependenciesCount) {
                        computedObservable.dispose();
                        changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state
                    } else {
                        changed = computedObservable.isDifferent(state.latestValue, newValue);
                    }

                    if (changed) {
                        if (!state.isSleeping) {
                            computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
                        } else {
                            computedObservable.updateVersion();
                        }

                        state.latestValue = newValue;
                        if (DEBUG) computedObservable._latestValue = newValue;

                        computedObservable["notifySubscribers"](state.latestValue, "spectate");

                        if (!state.isSleeping && notifyChange) {
                            computedObservable["notifySubscribers"](state.latestValue);
                        }
                        if (computedObservable._recordUpdate) {
                            computedObservable._recordUpdate();
                        }
                    }

                    if (isInitial) {
                        computedObservable["notifySubscribers"](state.latestValue, "awake");
                    }

                    return changed;
                },
                evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {
                    // This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.
                    // You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection
                    // can be independent of try/finally blocks, which contributes to saving about 40% off the CPU
                    // overhead of computed evaluation (on V8 at least).

                    try {
                        var readFunction = state.readFunction;
                        return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
                    } finally {
                        ko.dependencyDetection.end();

                        // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
                        if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
                            ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
                        }

                        state.isStale = state.isDirty = false;
                    }
                },
                peek: function (evaluate) {
                    // By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
                    // Pass in true to evaluate if needed.
                    var state = this[computedState];
                    if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {
                        this.evaluateImmediate();
                    }
                    return state.latestValue;
                },
                limit: function (limitFunction) {
                    // Override the limit function with one that delays evaluation as well
                    ko.subscribable['fn'].limit.call(this, limitFunction);
                    this._evalIfChanged = function () {
                        if (!this[computedState].isSleeping) {
                            if (this[computedState].isStale) {
                                this.evaluateImmediate();
                            } else {
                                this[computedState].isDirty = false;
                            }
                        }
                        return this[computedState].latestValue;
                    };
                    this._evalDelayed = function (isChange) {
                        this._limitBeforeChange(this[computedState].latestValue);

                        // Mark as dirty
                        this[computedState].isDirty = true;
                        if (isChange) {
                            this[computedState].isStale = true;
                        }

                        // Pass the observable to the "limit" code, which will evaluate it when
                        // it's time to do the notification.
                        this._limitChange(this, !isChange /* isDirty */);
                    };
                },
                dispose: function () {
                    var state = this[computedState];
                    if (!state.isSleeping && state.dependencyTracking) {
                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                            if (dependency.dispose)
                                dependency.dispose();
                        });
                    }
                    if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
                        ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
                    }
                    state.dependencyTracking = undefined;
                    state.dependenciesCount = 0;
                    state.isDisposed = true;
                    state.isStale = false;
                    state.isDirty = false;
                    state.isSleeping = false;
                    state.disposeWhenNodeIsRemoved = undefined;
                    state.disposeWhen = undefined;
                    state.readFunction = undefined;
                    if (!this.hasWriteFunction) {
                        state.evaluatorFunctionTarget = undefined;
                    }
                }
            };

            var pureComputedOverrides = {
                beforeSubscriptionAdd: function (event) {
                    // If asleep, wake up the computed by subscribing to any dependencies.
                    var computedObservable = this,
                        state = computedObservable[computedState];
                    if (!state.isDisposed && state.isSleeping && event == 'change') {
                        state.isSleeping = false;
                        if (state.isStale || computedObservable.haveDependenciesChanged()) {
                            state.dependencyTracking = null;
                            state.dependenciesCount = 0;
                            if (computedObservable.evaluateImmediate()) {
                                computedObservable.updateVersion();
                            }
                        } else {
                            // First put the dependencies in order
                            var dependenciesOrder = [];
                            ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                                dependenciesOrder[dependency._order] = id;
                            });
                            // Next, subscribe to each one
                            ko.utils.arrayForEach(dependenciesOrder, function (id, order) {
                                var dependency = state.dependencyTracking[id],
                                    subscription = computedObservable.subscribeToDependency(dependency._target);
                                subscription._order = order;
                                subscription._version = dependency._version;
                                state.dependencyTracking[id] = subscription;
                            });
                            // Waking dependencies may have triggered effects
                            if (computedObservable.haveDependenciesChanged()) {
                                if (computedObservable.evaluateImmediate()) {
                                    computedObservable.updateVersion();
                                }
                            }
                        }

                        if (!state.isDisposed) {     // test since evaluating could trigger disposal
                            computedObservable["notifySubscribers"](state.latestValue, "awake");
                        }
                    }
                },
                afterSubscriptionRemove: function (event) {
                    var state = this[computedState];
                    if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                            if (dependency.dispose) {
                                state.dependencyTracking[id] = {
                                    _target: dependency._target,
                                    _order: dependency._order,
                                    _version: dependency._version
                                };
                                dependency.dispose();
                            }
                        });
                        state.isSleeping = true;
                        this["notifySubscribers"](undefined, "asleep");
                    }
                },
                getVersion: function () {
                    // Because a pure computed is not automatically updated while it is sleeping, we can't
                    // simply return the version number. Instead, we check if any of the dependencies have
                    // changed and conditionally re-evaluate the computed observable.
                    var state = this[computedState];
                    if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {
                        this.evaluateImmediate();
                    }
                    return ko.subscribable['fn'].getVersion.call(this);
                }
            };

            var deferEvaluationOverrides = {
                beforeSubscriptionAdd: function (event) {
                    // This will force a computed with deferEvaluation to evaluate when the first subscription is registered.
                    if (event == 'change' || event == 'beforeChange') {
                        this.peek();
                    }
                }
            };

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.computed constructor
            if (ko.utils.canSetPrototype) {
                ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);
            }

// Set the proto values for ko.computed
            var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
            computedFn[protoProp] = ko.computed;

            ko.isComputed = function (instance) {
                return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
            };

            ko.isPureComputed = function (instance) {
                return ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;
            };

            ko.exportSymbol('computed', ko.computed);
            ko.exportSymbol('dependentObservable', ko.computed);    // export ko.dependentObservable for backwards compatibility (1.x)
            ko.exportSymbol('isComputed', ko.isComputed);
            ko.exportSymbol('isPureComputed', ko.isPureComputed);
            ko.exportSymbol('computed.fn', computedFn);
            ko.exportProperty(computedFn, 'peek', computedFn.peek);
            ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
            ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
            ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
            ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);

            ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {
                if (typeof evaluatorFunctionOrOptions === 'function') {
                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true});
                } else {
                    evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions);   // make a copy of the parameter object
                    evaluatorFunctionOrOptions['pure'] = true;
                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
                }
            }
            ko.exportSymbol('pureComputed', ko.pureComputed);

            (function() {
                var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)

                ko.toJS = function(rootObject) {
                    if (arguments.length == 0)
                        throw new Error("When calling ko.toJS, pass the object you want to convert.");

                    // We just unwrap everything at every level in the object graph
                    return mapJsObjectGraph(rootObject, function(valueToMap) {
                        // Loop because an observable's value might in turn be another observable wrapper
                        for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
                            valueToMap = valueToMap();
                        return valueToMap;
                    });
                };

                ko.toJSON = function(rootObject, replacer, space) {     // replacer and space are optional
                    var plainJavaScriptObject = ko.toJS(rootObject);
                    return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
                };

                function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
                    visitedObjects = visitedObjects || new objectLookup();

                    rootObject = mapInputCallback(rootObject);
                    var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
                    if (!canHaveProperties)
                        return rootObject;

                    var outputProperties = rootObject instanceof Array ? [] : {};
                    visitedObjects.save(rootObject, outputProperties);

                    visitPropertiesOrArrayEntries(rootObject, function(indexer) {
                        var propertyValue = mapInputCallback(rootObject[indexer]);

                        switch (typeof propertyValue) {
                            case "boolean":
                            case "number":
                            case "string":
                            case "function":
                                outputProperties[indexer] = propertyValue;
                                break;
                            case "object":
                            case "undefined":
                                var previouslyMappedValue = visitedObjects.get(propertyValue);
                                outputProperties[indexer] = (previouslyMappedValue !== undefined)
                                    ? previouslyMappedValue
                                    : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
                                break;
                        }
                    });

                    return outputProperties;
                }

                function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
                    if (rootObject instanceof Array) {
                        for (var i = 0; i < rootObject.length; i++)
                            visitorCallback(i);

                        // For arrays, also respect toJSON property for custom mappings (fixes #278)
                        if (typeof rootObject['toJSON'] == 'function')
                            visitorCallback('toJSON');
                    } else {
                        for (var propertyName in rootObject) {
                            visitorCallback(propertyName);
                        }
                    }
                };

                function objectLookup() {
                    this.keys = [];
                    this.values = [];
                };

                objectLookup.prototype = {
                    constructor: objectLookup,
                    save: function(key, value) {
                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
                        if (existingIndex >= 0)
                            this.values[existingIndex] = value;
                        else {
                            this.keys.push(key);
                            this.values.push(value);
                        }
                    },
                    get: function(key) {
                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
                        return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
                    }
                };
            })();

            ko.exportSymbol('toJS', ko.toJS);
            ko.exportSymbol('toJSON', ko.toJSON);
            ko.when = function(predicate, callback, context) {
                function kowhen (resolve) {
                    var observable = ko.pureComputed(predicate, context).extend({notify:'always'});
                    var subscription = observable.subscribe(function(value) {
                        if (value) {
                            subscription.dispose();
                            resolve(value);
                        }
                    });
                    // In case the initial value is true, process it right away
                    observable['notifySubscribers'](observable.peek());

                    return subscription;
                }
                if (typeof Promise === "function" && !callback) {
                    return new Promise(kowhen);
                } else {
                    return kowhen(callback.bind(context));
                }
            };

            ko.exportSymbol('when', ko.when);
            (function () {
                var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';

                // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
                // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
                // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
                ko.selectExtensions = {
                    readValue : function(element) {
                        switch (ko.utils.tagNameLower(element)) {
                            case 'option':
                                if (element[hasDomDataExpandoProperty] === true)
                                    return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
                                return ko.utils.ieVersion <= 7
                                    ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
                                    : element.value;
                            case 'select':
                                return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
                            default:
                                return element.value;
                        }
                    },

                    writeValue: function(element, value, allowUnset) {
                        switch (ko.utils.tagNameLower(element)) {
                            case 'option':
                                if (typeof value === "string") {
                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
                                    if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
                                        delete element[hasDomDataExpandoProperty];
                                    }
                                    element.value = value;
                                }
                                else {
                                    // Store arbitrary object using DomData
                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
                                    element[hasDomDataExpandoProperty] = true;

                                    // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
                                    element.value = typeof value === "number" ? value : "";
                                }
                                break;
                            case 'select':
                                if (value === "" || value === null)       // A blank string or null value will select the caption
                                    value = undefined;
                                var selection = -1;
                                for (var i = 0, n = element.options.length, optionValue; i < n; ++i) {
                                    optionValue = ko.selectExtensions.readValue(element.options[i]);
                                    // Include special check to handle selecting a caption with a blank string value
                                    if (optionValue == value || (optionValue === "" && value === undefined)) {
                                        selection = i;
                                        break;
                                    }
                                }
                                if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {
                                    element.selectedIndex = selection;
                                    if (ko.utils.ieVersion === 6) {
                                        // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
                                        // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
                                        // to apply the value as well.
                                        ko.utils.setTimeout(function () {
                                            element.selectedIndex = selection;
                                        }, 0);
                                    }
                                }
                                break;
                            default:
                                if ((value === null) || (value === undefined))
                                    value = "";
                                element.value = value;
                                break;
                        }
                    }
                };
            })();

            ko.exportSymbol('selectExtensions', ko.selectExtensions);
            ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
            ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
            ko.expressionRewriting = (function () {
                var javaScriptReservedWords = ["true", "false", "null", "undefined"];

                // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
                // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
                // This also will not properly handle nested brackets (e.g., obj1[obj2['prop']]; see #911).
                var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;

                function getWriteableValue(expression) {
                    if (ko.utils.arrayIndexOf(javaScriptReservedWords, expression) >= 0)
                        return false;
                    var match = expression.match(javaScriptAssignmentTarget);
                    return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
                }

                // The following regular expressions will be used to split an object-literal string into tokens

                var specials = ',"\'`{}()/:[\\]',    // These characters have special meaning to the parser and must not appear in the middle of a token, except as part of a string.
                    // Create the actual regular expression by or-ing the following regex strings. The order is important.
                    bindingToken = RegExp([
                        // These match strings, either with double quotes, single quotes, or backticks
                        '"(?:\\\\.|[^"])*"',
                        "'(?:\\\\.|[^'])*'",
                        "`(?:\\\\.|[^`])*`",
                        // Match C style comments
                        "/\\*(?:[^*]|\\*+[^*/])*\\*+/",
                        // Match C++ style comments
                        "//.*\n",
                        // Match a regular expression (text enclosed by slashes), but will also match sets of divisions
                        // as a regular expression (this is handled by the parsing loop below).
                        '/(?:\\\\.|[^/])+/\w*',
                        // Match text (at least two characters) that does not contain any of the above special characters,
                        // although some of the special characters are allowed to start it (all but the colon and comma).
                        // The text can contain spaces, but leading or trailing spaces are skipped.
                        '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']',
                        // Match any non-space character not matched already. This will match colons and commas, since they're
                        // not matched by "everyThingElse", but will also match any other single character that wasn't already
                        // matched (for example: in "a: 1, b: 2", each of the non-space characters will be matched by oneNotSpace).
                        '[^\\s]'
                    ].join('|'), 'g'),

                    // Match end of previous token to determine whether a slash is a division or regex.
                    divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/,
                    keywordRegexLookBehind = {'in':1,'return':1,'typeof':1};

                function parseObjectLiteral(objectLiteralString) {
                    // Trim leading and trailing spaces from the string
                    var str = ko.utils.stringTrim(objectLiteralString);

                    // Trim braces '{' surrounding the whole object literal
                    if (str.charCodeAt(0) === 123) str = str.slice(1, -1);

                    // Add a newline to correctly match a C++ style comment at the end of the string and
                    // add a comma so that we don't need a separate code block to deal with the last item
                    str += "\n,";

                    // Split into tokens
                    var result = [], toks = str.match(bindingToken), key, values = [], depth = 0;

                    if (toks.length > 1) {
                        for (var i = 0, tok; tok = toks[i]; ++i) {
                            var c = tok.charCodeAt(0);
                            // A comma signals the end of a key/value pair if depth is zero
                            if (c === 44) { // ","
                                if (depth <= 0) {
                                    result.push((key && values.length) ? {key: key, value: values.join('')} : {'unknown': key || values.join('')});
                                    key = depth = 0;
                                    values = [];
                                    continue;
                                }
                                // Simply skip the colon that separates the name and value
                            } else if (c === 58) { // ":"
                                if (!depth && !key && values.length === 1) {
                                    key = values.pop();
                                    continue;
                                }
                                // Comments: skip them
                            } else if (c === 47 && tok.length > 1 && (tok.charCodeAt(1) === 47 || tok.charCodeAt(1) === 42)) {  // "//" or "/*"
                                continue;
                                // A set of slashes is initially matched as a regular expression, but could be division
                            } else if (c === 47 && i && tok.length > 1) {  // "/"
                                // Look at the end of the previous token to determine if the slash is actually division
                                var match = toks[i-1].match(divisionLookBehind);
                                if (match && !keywordRegexLookBehind[match[0]]) {
                                    // The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash)
                                    str = str.substr(str.indexOf(tok) + 1);
                                    toks = str.match(bindingToken);
                                    i = -1;
                                    // Continue with just the slash
                                    tok = '/';
                                }
                                // Increment depth for parentheses, braces, and brackets so that interior commas are ignored
                            } else if (c === 40 || c === 123 || c === 91) { // '(', '{', '['
                                ++depth;
                            } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'
                                --depth;
                                // The key will be the first token; if it's a string, trim the quotes
                            } else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'"
                                tok = tok.slice(1, -1);
                            }
                            values.push(tok);
                        }
                        if (depth > 0) {
                            throw Error("Unbalanced parentheses, braces, or brackets");
                        }
                    }
                    return result;
                }

                // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable.
                var twoWayBindings = {};

                function preProcessBindings(bindingsStringOrKeyValueArray, bindingOptions) {
                    bindingOptions = bindingOptions || {};

                    function processKeyValue(key, val) {
                        var writableVal;
                        function callPreprocessHook(obj) {
                            return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true;
                        }
                        if (!bindingParams) {
                            if (!callPreprocessHook(ko['getBindingHandler'](key)))
                                return;

                            if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
                                // For two-way bindings, provide a write method in case the value
                                // isn't a writable observable.
                                var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;
                                propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}");
                            }
                        }
                        // Values are wrapped in a function so that each value can be accessed independently
                        if (makeValueAccessors) {
                            val = 'function(){return ' + val + ' }';
                        }
                        resultStrings.push("'" + key + "':" + val);
                    }

                    var resultStrings = [],
                        propertyAccessorResultStrings = [],
                        makeValueAccessors = bindingOptions['valueAccessors'],
                        bindingParams = bindingOptions['bindingParams'],
                        keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ?
                            parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;

                    ko.utils.arrayForEach(keyValueArray, function(keyValue) {
                        processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value);
                    });

                    if (propertyAccessorResultStrings.length)
                        processKeyValue('_ko_property_writers', "{" + propertyAccessorResultStrings.join(",") + " }");

                    return resultStrings.join(",");
                }

                return {
                    bindingRewriteValidators: [],

                    twoWayBindings: twoWayBindings,

                    parseObjectLiteral: parseObjectLiteral,

                    preProcessBindings: preProcessBindings,

                    keyValueArrayContainsKey: function(keyValueArray, key) {
                        for (var i = 0; i < keyValueArray.length; i++)
                            if (keyValueArray[i]['key'] == key)
                                return true;
                        return false;
                    },

                    // Internal, private KO utility for updating model properties from within bindings
                    // property:            If the property being updated is (or might be) an observable, pass it here
                    //                      If it turns out to be a writable observable, it will be written to directly
                    // allBindings:         An object with a get method to retrieve bindings in the current execution context.
                    //                      This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
                    // key:                 The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
                    // value:               The value to be written
                    // checkIfDifferent:    If true, and if the property being written is a writable observable, the value will only be written if
                    //                      it is !== existing value on that writable observable
                    writeValueToProperty: function(property, allBindings, key, value, checkIfDifferent) {
                        if (!property || !ko.isObservable(property)) {
                            var propWriters = allBindings.get('_ko_property_writers');
                            if (propWriters && propWriters[key])
                                propWriters[key](value);
                        } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
                            property(value);
                        }
                    }
                };
            })();

            ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
            ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
            ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
            ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);

// Making bindings explicitly declare themselves as "two way" isn't ideal in the long term (it would be better if
// all bindings could use an official 'property writer' API without needing to declare that they might). However,
// since this is not, and has never been, a public API (_ko_property_writers was never documented), it's acceptable
// as an internal implementation detail in the short term.
// For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an
// undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official
// public API, and we reserve the right to remove it at any time if we create a real public property writers API.
            ko.exportSymbol('expressionRewriting._twoWayBindings', ko.expressionRewriting.twoWayBindings);

// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
            ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
            ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);
            (function() {
                // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
                // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
                // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
                // of that virtual hierarchy
                //
                // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
                // without having to scatter special cases all over the binding and templating code.

                // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
                // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
                // So, use node.text where available, and node.nodeValue elsewhere
                var commentNodesHaveTextProperty = document && document.createComment("test").text === "<!--test-->";

                var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
                var endCommentRegex =   commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
                var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };

                function isStartComment(node) {
                    return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
                }

                function isEndComment(node) {
                    return (node.nodeType == 8) && endCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
                }

                function isUnmatchedEndComment(node) {
                    return isEndComment(node) && !(ko.utils.domData.get(node, matchedEndCommentDataKey));
                }

                var matchedEndCommentDataKey = "__ko_matchedEndComment__"

                function getVirtualChildren(startComment, allowUnbalanced) {
                    var currentNode = startComment;
                    var depth = 1;
                    var children = [];
                    while (currentNode = currentNode.nextSibling) {
                        if (isEndComment(currentNode)) {
                            ko.utils.domData.set(currentNode, matchedEndCommentDataKey, true);
                            depth--;
                            if (depth === 0)
                                return children;
                        }

                        children.push(currentNode);

                        if (isStartComment(currentNode))
                            depth++;
                    }
                    if (!allowUnbalanced)
                        throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
                    return null;
                }

                function getMatchingEndComment(startComment, allowUnbalanced) {
                    var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
                    if (allVirtualChildren) {
                        if (allVirtualChildren.length > 0)
                            return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
                        return startComment.nextSibling;
                    } else
                        return null; // Must have no matching end comment, and allowUnbalanced is true
                }

                function getUnbalancedChildTags(node) {
                    // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
                    //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->
                    var childNode = node.firstChild, captureRemaining = null;
                    if (childNode) {
                        do {
                            if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes
                                captureRemaining.push(childNode);
                            else if (isStartComment(childNode)) {
                                var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
                                if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set
                                    childNode = matchingEndComment;
                                else
                                    captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
                            } else if (isEndComment(childNode)) {
                                captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
                            }
                        } while (childNode = childNode.nextSibling);
                    }
                    return captureRemaining;
                }

                ko.virtualElements = {
                    allowedBindings: {},

                    childNodes: function(node) {
                        return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
                    },

                    emptyNode: function(node) {
                        if (!isStartComment(node))
                            ko.utils.emptyDomNode(node);
                        else {
                            var virtualChildren = ko.virtualElements.childNodes(node);
                            for (var i = 0, j = virtualChildren.length; i < j; i++)
                                ko.removeNode(virtualChildren[i]);
                        }
                    },

                    setDomNodeChildren: function(node, childNodes) {
                        if (!isStartComment(node))
                            ko.utils.setDomNodeChildren(node, childNodes);
                        else {
                            ko.virtualElements.emptyNode(node);
                            var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
                            for (var i = 0, j = childNodes.length; i < j; i++)
                                endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
                        }
                    },

                    prepend: function(containerNode, nodeToPrepend) {
                        var insertBeforeNode;

                        if (isStartComment(containerNode)) {
                            // Start comments must always have a parent and at least one following sibling (the end comment)
                            insertBeforeNode = containerNode.nextSibling;
                            containerNode = containerNode.parentNode;
                        } else {
                            insertBeforeNode = containerNode.firstChild;
                        }

                        if (!insertBeforeNode) {
                            containerNode.appendChild(nodeToPrepend);
                        } else if (nodeToPrepend !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
                            containerNode.insertBefore(nodeToPrepend, insertBeforeNode);
                        }
                    },

                    insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
                        if (!insertAfterNode) {
                            ko.virtualElements.prepend(containerNode, nodeToInsert);
                        } else {
                            // Children of start comments must always have a parent and at least one following sibling (the end comment)
                            var insertBeforeNode = insertAfterNode.nextSibling;

                            if (isStartComment(containerNode)) {
                                containerNode = containerNode.parentNode;
                            }

                            if (!insertBeforeNode) {
                                containerNode.appendChild(nodeToInsert);
                            } else if (nodeToInsert !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
                                containerNode.insertBefore(nodeToInsert, insertBeforeNode);
                            }
                        }
                    },

                    firstChild: function(node) {
                        if (!isStartComment(node)) {
                            if (node.firstChild && isEndComment(node.firstChild)) {
                                throw new Error("Found invalid end comment, as the first child of " + node);
                            }
                            return node.firstChild;
                        } else if (!node.nextSibling || isEndComment(node.nextSibling)) {
                            return null;
                        } else {
                            return node.nextSibling;
                        }
                    },

                    nextSibling: function(node) {
                        if (isStartComment(node)) {
                            node = getMatchingEndComment(node);
                        }

                        if (node.nextSibling && isEndComment(node.nextSibling)) {
                            if (isUnmatchedEndComment(node.nextSibling)) {
                                throw Error("Found end comment without a matching opening comment, as child of " + node);
                            } else {
                                return null;
                            }
                        } else {
                            return node.nextSibling;
                        }
                    },

                    hasBindingValue: isStartComment,

                    virtualNodeBindingValue: function(node) {
                        var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
                        return regexMatch ? regexMatch[1] : null;
                    },

                    normaliseVirtualElementDomStructure: function(elementVerified) {
                        // Workaround for https://github.com/SteveSanderson/knockout/issues/155
                        // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
                        // that are direct descendants of <ul> into the preceding <li>)
                        if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
                            return;

                        // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
                        // must be intended to appear *after* that child, so move them there.
                        var childNode = elementVerified.firstChild;
                        if (childNode) {
                            do {
                                if (childNode.nodeType === 1) {
                                    var unbalancedTags = getUnbalancedChildTags(childNode);
                                    if (unbalancedTags) {
                                        // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
                                        var nodeToInsertBefore = childNode.nextSibling;
                                        for (var i = 0; i < unbalancedTags.length; i++) {
                                            if (nodeToInsertBefore)
                                                elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
                                            else
                                                elementVerified.appendChild(unbalancedTags[i]);
                                        }
                                    }
                                }
                            } while (childNode = childNode.nextSibling);
                        }
                    }
                };
            })();
            ko.exportSymbol('virtualElements', ko.virtualElements);
            ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
            ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified
            ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified
            ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
            ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
            (function() {
                var defaultBindingAttributeName = "data-bind";

                ko.bindingProvider = function() {
                    this.bindingCache = {};
                };

                ko.utils.extend(ko.bindingProvider.prototype, {
                    'nodeHasBindings': function(node) {
                        switch (node.nodeType) {
                            case 1: // Element
                                return node.getAttribute(defaultBindingAttributeName) != null
                                    || ko.components['getComponentNameForNode'](node);
                            case 8: // Comment node
                                return ko.virtualElements.hasBindingValue(node);
                            default: return false;
                        }
                    },

                    'getBindings': function(node, bindingContext) {
                        var bindingsString = this['getBindingsString'](node, bindingContext),
                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ false);
                    },

                    'getBindingAccessors': function(node, bindingContext) {
                        var bindingsString = this['getBindingsString'](node, bindingContext),
                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node, { 'valueAccessors': true }) : null;
                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ true);
                    },

                    // The following function is only used internally by this default provider.
                    // It's not part of the interface definition for a general binding provider.
                    'getBindingsString': function(node, bindingContext) {
                        switch (node.nodeType) {
                            case 1: return node.getAttribute(defaultBindingAttributeName);   // Element
                            case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
                            default: return null;
                        }
                    },

                    // The following function is only used internally by this default provider.
                    // It's not part of the interface definition for a general binding provider.
                    'parseBindingsString': function(bindingsString, bindingContext, node, options) {
                        try {
                            var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache, options);
                            return bindingFunction(bindingContext, node);
                        } catch (ex) {
                            ex.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + ex.message;
                            throw ex;
                        }
                    }
                });

                ko.bindingProvider['instance'] = new ko.bindingProvider();

                function createBindingsStringEvaluatorViaCache(bindingsString, cache, options) {
                    var cacheKey = bindingsString + (options && options['valueAccessors'] || '');
                    return cache[cacheKey]
                        || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));
                }

                function createBindingsStringEvaluator(bindingsString, options) {
                    // Build the source for a function that evaluates "expression"
                    // For each scope variable, add an extra level of "with" nesting
                    // Example result: with(sc1) { with(sc0) { return (expression) } }
                    var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),
                        functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
                    return new Function("$context", "$element", functionBody);
                }
            })();

            ko.exportSymbol('bindingProvider', ko.bindingProvider);
            (function () {
                // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294
                var contextSubscribable = ko.utils.createSymbolOrString('_subscribable');
                var contextAncestorBindingInfo = ko.utils.createSymbolOrString('_ancestorBindingInfo');
                var contextDataDependency = ko.utils.createSymbolOrString('_dataDependency');

                ko.bindingHandlers = {};

                // The following element types will not be recursed into during binding.
                var bindingDoesNotRecurseIntoElementTypes = {
                    // Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,
                    // because it's unexpected and a potential XSS issue.
                    // Also bindings should not operate on <template> elements since this breaks in Internet Explorer
                    // and because such elements' contents are always intended to be bound in a different context
                    // from where they appear in the document.
                    'script': true,
                    'textarea': true,
                    'template': true
                };

                // Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers
                ko['getBindingHandler'] = function(bindingKey) {
                    return ko.bindingHandlers[bindingKey];
                };

                var inheritParentVm = {};

                // The ko.bindingContext constructor is only called directly to create the root context. For child
                // contexts, use bindingContext.createChildContext or bindingContext.extend.
                ko.bindingContext = function(dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) {

                    // The binding context object includes static properties for the current, parent, and root view models.
                    // If a view model is actually stored in an observable, the corresponding binding context object, and
                    // any child contexts, must be updated when the view model is changed.
                    function updateContext() {
                        // Most of the time, the context will directly get a view model object, but if a function is given,
                        // we call the function to retrieve the view model. If the function accesses any observables or returns
                        // an observable, the dependency is tracked, and those observables can later cause the binding
                        // context to be updated.
                        var dataItemOrObservable = isFunc ? realDataItemOrAccessor() : realDataItemOrAccessor,
                            dataItem = ko.utils.unwrapObservable(dataItemOrObservable);

                        if (parentContext) {
                            // Copy $root and any custom properties from the parent context
                            ko.utils.extend(self, parentContext);

                            // Copy Symbol properties
                            if (contextAncestorBindingInfo in parentContext) {
                                self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo];
                            }
                        } else {
                            self['$parents'] = [];
                            self['$root'] = dataItem;

                            // Export 'ko' in the binding context so it will be available in bindings and templates
                            // even if 'ko' isn't exported as a global, such as when using an AMD loader.
                            // See https://github.com/SteveSanderson/knockout/issues/490
                            self['ko'] = ko;
                        }

                        self[contextSubscribable] = subscribable;

                        if (shouldInheritData) {
                            dataItem = self['$data'];
                        } else {
                            self['$rawData'] = dataItemOrObservable;
                            self['$data'] = dataItem;
                        }

                        if (dataItemAlias)
                            self[dataItemAlias] = dataItem;

                        // The extendCallback function is provided when creating a child context or extending a context.
                        // It handles the specific actions needed to finish setting up the binding context. Actions in this
                        // function could also add dependencies to this binding context.
                        if (extendCallback)
                            extendCallback(self, parentContext, dataItem);

                        // When a "parent" context is given and we don't already have a dependency on its context, register a dependency on it.
                        // Thus whenever the parent context is updated, this context will also be updated.
                        if (parentContext && parentContext[contextSubscribable] && !ko.computedContext.computed().hasAncestorDependency(parentContext[contextSubscribable])) {
                            parentContext[contextSubscribable]();
                        }

                        if (dataDependency) {
                            self[contextDataDependency] = dataDependency;
                        }

                        return self['$data'];
                    }

                    var self = this,
                        shouldInheritData = dataItemOrAccessor === inheritParentVm,
                        realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor,
                        isFunc = typeof(realDataItemOrAccessor) == "function" && !ko.isObservable(realDataItemOrAccessor),
                        nodes,
                        subscribable,
                        dataDependency = options && options['dataDependency'];

                    if (options && options['exportDependencies']) {
                        // The "exportDependencies" option means that the calling code will track any dependencies and re-create
                        // the binding context when they change.
                        updateContext();
                    } else {
                        subscribable = ko.pureComputed(updateContext);
                        subscribable.peek();

                        // At this point, the binding context has been initialized, and the "subscribable" computed observable is
                        // subscribed to any observables that were accessed in the process. If there is nothing to track, the
                        // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in
                        // the context object.
                        if (subscribable.isActive()) {
                            // Always notify because even if the model ($data) hasn't changed, other context properties might have changed
                            subscribable['equalityComparer'] = null;
                        } else {
                            self[contextSubscribable] = undefined;
                        }
                    }
                }

                // Extend the binding context hierarchy with a new view model object. If the parent context is watching
                // any observables, the new child context will automatically get a dependency on the parent context.
                // But this does not mean that the $data value of the child context will also get updated. If the child
                // view model also depends on the parent view model, you must provide a function that returns the correct
                // view model on each update.
                ko.bindingContext.prototype['createChildContext'] = function (dataItemOrAccessor, dataItemAlias, extendCallback, options) {
                    if (!options && dataItemAlias && typeof dataItemAlias == "object") {
                        options = dataItemAlias;
                        dataItemAlias = options['as'];
                        extendCallback = options['extend'];
                    }

                    if (dataItemAlias && options && options['noChildContext']) {
                        var isFunc = typeof(dataItemOrAccessor) == "function" && !ko.isObservable(dataItemOrAccessor);
                        return new ko.bindingContext(inheritParentVm, this, null, function (self) {
                            if (extendCallback)
                                extendCallback(self);
                            self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor;
                        }, options);
                    }

                    return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, function (self, parentContext) {
                        // Extend the context hierarchy by setting the appropriate pointers
                        self['$parentContext'] = parentContext;
                        self['$parent'] = parentContext['$data'];
                        self['$parents'] = (parentContext['$parents'] || []).slice(0);
                        self['$parents'].unshift(self['$parent']);
                        if (extendCallback)
                            extendCallback(self);
                    }, options);
                };

                // Extend the binding context with new custom properties. This doesn't change the context hierarchy.
                // Similarly to "child" contexts, provide a function here to make sure that the correct values are set
                // when an observable view model is updated.
                ko.bindingContext.prototype['extend'] = function(properties, options) {
                    return new ko.bindingContext(inheritParentVm, this, null, function(self, parentContext) {
                        ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties);
                    }, options);
                };

                var boundElementDomDataKey = ko.utils.domData.nextKey();

                function asyncContextDispose(node) {
                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey),
                        asyncContext = bindingInfo && bindingInfo.asyncContext;
                    if (asyncContext) {
                        bindingInfo.asyncContext = null;
                        asyncContext.notifyAncestor();
                    }
                }
                function AsyncCompleteContext(node, bindingInfo, ancestorBindingInfo) {
                    this.node = node;
                    this.bindingInfo = bindingInfo;
                    this.asyncDescendants = [];
                    this.childrenComplete = false;

                    if (!bindingInfo.asyncContext) {
                        ko.utils.domNodeDisposal.addDisposeCallback(node, asyncContextDispose);
                    }

                    if (ancestorBindingInfo && ancestorBindingInfo.asyncContext) {
                        ancestorBindingInfo.asyncContext.asyncDescendants.push(node);
                        this.ancestorBindingInfo = ancestorBindingInfo;
                    }
                }
                AsyncCompleteContext.prototype.notifyAncestor = function () {
                    if (this.ancestorBindingInfo && this.ancestorBindingInfo.asyncContext) {
                        this.ancestorBindingInfo.asyncContext.descendantComplete(this.node);
                    }
                };
                AsyncCompleteContext.prototype.descendantComplete = function (node) {
                    ko.utils.arrayRemoveItem(this.asyncDescendants, node);
                    if (!this.asyncDescendants.length && this.childrenComplete) {
                        this.completeChildren();
                    }
                };
                AsyncCompleteContext.prototype.completeChildren = function () {
                    this.childrenComplete = true;
                    if (this.bindingInfo.asyncContext && !this.asyncDescendants.length) {
                        this.bindingInfo.asyncContext = null;
                        ko.utils.domNodeDisposal.removeDisposeCallback(this.node, asyncContextDispose);
                        ko.bindingEvent.notify(this.node, ko.bindingEvent.descendantsComplete);
                        this.notifyAncestor();
                    }
                };

                ko.bindingEvent = {
                    childrenComplete: "childrenComplete",
                    descendantsComplete : "descendantsComplete",

                    subscribe: function (node, event, callback, context, options) {
                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
                        if (!bindingInfo.eventSubscribable) {
                            bindingInfo.eventSubscribable = new ko.subscribable;
                        }
                        if (options && options['notifyImmediately'] && bindingInfo.notifiedEvents[event]) {
                            ko.dependencyDetection.ignore(callback, context, [node]);
                        }
                        return bindingInfo.eventSubscribable.subscribe(callback, context, event);
                    },

                    notify: function (node, event) {
                        var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
                        if (bindingInfo) {
                            bindingInfo.notifiedEvents[event] = true;
                            if (bindingInfo.eventSubscribable) {
                                bindingInfo.eventSubscribable['notifySubscribers'](node, event);
                            }
                            if (event == ko.bindingEvent.childrenComplete) {
                                if (bindingInfo.asyncContext) {
                                    bindingInfo.asyncContext.completeChildren();
                                } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable && bindingInfo.eventSubscribable.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) {
                                    // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously.
                                    // That's because without the asyncContext, we don't have a way to know that all descendants have completed.
                                    throw new Error("descendantsComplete event not supported for bindings on this node");
                                }
                            }
                        }
                    },

                    startPossiblyAsyncContentBinding: function (node, bindingContext) {
                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});

                        if (!bindingInfo.asyncContext) {
                            bindingInfo.asyncContext = new AsyncCompleteContext(node, bindingInfo, bindingContext[contextAncestorBindingInfo]);
                        }

                        // If the provided context was already extended with this node's binding info, just return the extended context
                        if (bindingContext[contextAncestorBindingInfo] == bindingInfo) {
                            return bindingContext;
                        }

                        return bindingContext['extend'](function (ctx) {
                            ctx[contextAncestorBindingInfo] = bindingInfo;
                        });
                    }
                };

                // Returns the valueAccessor function for a binding value
                function makeValueAccessor(value) {
                    return function() {
                        return value;
                    };
                }

                // Returns the value of a valueAccessor function
                function evaluateValueAccessor(valueAccessor) {
                    return valueAccessor();
                }

                // Given a function that returns bindings, create and return a new object that contains
                // binding value-accessors functions. Each accessor function calls the original function
                // so that it always gets the latest value and all dependencies are captured. This is used
                // by ko.applyBindingsToNode and getBindingsAndMakeAccessors.
                function makeAccessorsFromFunction(callback) {
                    return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), function(value, key) {
                        return function() {
                            return callback()[key];
                        };
                    });
                }

                // Given a bindings function or object, create and return a new object that contains
                // binding value-accessors functions. This is used by ko.applyBindingsToNode.
                function makeBindingAccessors(bindings, context, node) {
                    if (typeof bindings === 'function') {
                        return makeAccessorsFromFunction(bindings.bind(null, context, node));
                    } else {
                        return ko.utils.objectMap(bindings, makeValueAccessor);
                    }
                }

                // This function is used if the binding provider doesn't include a getBindingAccessors function.
                // It must be called with 'this' set to the provider instance.
                function getBindingsAndMakeAccessors(node, context) {
                    return makeAccessorsFromFunction(this['getBindings'].bind(this, node, context));
                }

                function validateThatBindingIsAllowedForVirtualElements(bindingName) {
                    var validator = ko.virtualElements.allowedBindings[bindingName];
                    if (!validator)
                        throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
                }

                function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) {
                    var nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);

                    if (nextInQueue) {
                        var currentChild,
                            provider = ko.bindingProvider['instance'],
                            preprocessNode = provider['preprocessNode'];

                        // Preprocessing allows a binding provider to mutate a node before bindings are applied to it. For example it's
                        // possible to insert new siblings after it, and/or replace the node with a different one. This can be used to
                        // implement custom binding syntaxes, such as {{ value }} for string interpolation, or custom element types that
                        // trigger insertion of <template> contents at that point in the document.
                        if (preprocessNode) {
                            while (currentChild = nextInQueue) {
                                nextInQueue = ko.virtualElements.nextSibling(currentChild);
                                preprocessNode.call(provider, currentChild);
                            }
                            // Reset nextInQueue for the next loop
                            nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
                        }

                        while (currentChild = nextInQueue) {
                            // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
                            nextInQueue = ko.virtualElements.nextSibling(currentChild);
                            applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);
                        }
                    }
                    ko.bindingEvent.notify(elementOrVirtualElement, ko.bindingEvent.childrenComplete);
                }

                function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) {
                    var bindingContextForDescendants = bindingContext;

                    var isElement = (nodeVerified.nodeType === 1);
                    if (isElement) // Workaround IE <= 8 HTML parsing weirdness
                        ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);

                    // Perf optimisation: Apply bindings only if...
                    // (1) We need to store the binding info for the node (all element nodes)
                    // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
                    var shouldApplyBindings = isElement || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified);
                    if (shouldApplyBindings)
                        bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants'];

                    if (bindingContextForDescendants && !bindingDoesNotRecurseIntoElementTypes[ko.utils.tagNameLower(nodeVerified)]) {
                        applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);
                    }
                }

                function topologicalSortBindings(bindings) {
                    // Depth-first sort
                    var result = [],                // The list of key/handler pairs that we will return
                        bindingsConsidered = {},    // A temporary record of which bindings are already in 'result'
                        cyclicDependencyStack = []; // Keeps track of a depth-search so that, if there's a cycle, we know which bindings caused it
                    ko.utils.objectForEach(bindings, function pushBinding(bindingKey) {
                        if (!bindingsConsidered[bindingKey]) {
                            var binding = ko['getBindingHandler'](bindingKey);
                            if (binding) {
                                // First add dependencies (if any) of the current binding
                                if (binding['after']) {
                                    cyclicDependencyStack.push(bindingKey);
                                    ko.utils.arrayForEach(binding['after'], function(bindingDependencyKey) {
                                        if (bindings[bindingDependencyKey]) {
                                            if (ko.utils.arrayIndexOf(cyclicDependencyStack, bindingDependencyKey) !== -1) {
                                                throw Error("Cannot combine the following bindings, because they have a cyclic dependency: " + cyclicDependencyStack.join(", "));
                                            } else {
                                                pushBinding(bindingDependencyKey);
                                            }
                                        }
                                    });
                                    cyclicDependencyStack.length--;
                                }
                                // Next add the current binding
                                result.push({ key: bindingKey, handler: binding });
                            }
                            bindingsConsidered[bindingKey] = true;
                        }
                    });

                    return result;
                }

                function applyBindingsToNodeInternal(node, sourceBindings, bindingContext) {
                    var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});

                    // Prevent multiple applyBindings calls for the same node, except when a binding value is specified
                    var alreadyBound = bindingInfo.alreadyBound;
                    if (!sourceBindings) {
                        if (alreadyBound) {
                            throw Error("You cannot apply bindings multiple times to the same element.");
                        }
                        bindingInfo.alreadyBound = true;
                    }
                    if (!alreadyBound) {
                        bindingInfo.context = bindingContext;
                    }
                    if (!bindingInfo.notifiedEvents) {
                        bindingInfo.notifiedEvents = {};
                    }

                    // Use bindings if given, otherwise fall back on asking the bindings provider to give us some bindings
                    var bindings;
                    if (sourceBindings && typeof sourceBindings !== 'function') {
                        bindings = sourceBindings;
                    } else {
                        var provider = ko.bindingProvider['instance'],
                            getBindings = provider['getBindingAccessors'] || getBindingsAndMakeAccessors;

                        // Get the binding from the provider within a computed observable so that we can update the bindings whenever
                        // the binding context is updated or if the binding provider accesses observables.
                        var bindingsUpdater = ko.dependentObservable(
                            function() {
                                bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);
                                // Register a dependency on the binding context to support observable view models.
                                if (bindings) {
                                    if (bindingContext[contextSubscribable]) {
                                        bindingContext[contextSubscribable]();
                                    }
                                    if (bindingContext[contextDataDependency]) {
                                        bindingContext[contextDataDependency]();
                                    }
                                }
                                return bindings;
                            },
                            null, { disposeWhenNodeIsRemoved: node }
                        );

                        if (!bindings || !bindingsUpdater.isActive())
                            bindingsUpdater = null;
                    }

                    var contextToExtend = bindingContext;
                    var bindingHandlerThatControlsDescendantBindings;
                    if (bindings) {
                        // Return the value accessor for a given binding. When bindings are static (won't be updated because of a binding
                        // context update), just return the value accessor from the binding. Otherwise, return a function that always gets
                        // the latest binding value and registers a dependency on the binding updater.
                        var getValueAccessor = bindingsUpdater
                            ? function(bindingKey) {
                                return function() {
                                    return evaluateValueAccessor(bindingsUpdater()[bindingKey]);
                                };
                            } : function(bindingKey) {
                                return bindings[bindingKey];
                            };

                        // Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated
                        function allBindings() {
                            return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);
                        }
                        // The following is the 3.x allBindings API
                        allBindings['get'] = function(key) {
                            return bindings[key] && evaluateValueAccessor(getValueAccessor(key));
                        };
                        allBindings['has'] = function(key) {
                            return key in bindings;
                        };

                        if (ko.bindingEvent.childrenComplete in bindings) {
                            ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, function () {
                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.childrenComplete]);
                                if (callback) {
                                    var nodes = ko.virtualElements.childNodes(node);
                                    if (nodes.length) {
                                        callback(nodes, ko.dataFor(nodes[0]));
                                    }
                                }
                            });
                        }

                        if (ko.bindingEvent.descendantsComplete in bindings) {
                            contextToExtend = ko.bindingEvent.startPossiblyAsyncContentBinding(node, bindingContext);
                            ko.bindingEvent.subscribe(node, ko.bindingEvent.descendantsComplete, function () {
                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.descendantsComplete]);
                                if (callback && ko.virtualElements.firstChild(node)) {
                                    callback(node);
                                }
                            });
                        }

                        // First put the bindings into the right order
                        var orderedBindings = topologicalSortBindings(bindings);

                        // Go through the sorted bindings, calling init and update for each
                        ko.utils.arrayForEach(orderedBindings, function(bindingKeyAndHandler) {
                            // Note that topologicalSortBindings has already filtered out any nonexistent binding handlers,
                            // so bindingKeyAndHandler.handler will always be nonnull.
                            var handlerInitFn = bindingKeyAndHandler.handler["init"],
                                handlerUpdateFn = bindingKeyAndHandler.handler["update"],
                                bindingKey = bindingKeyAndHandler.key;

                            if (node.nodeType === 8) {
                                validateThatBindingIsAllowedForVirtualElements(bindingKey);
                            }

                            try {
                                // Run init, ignoring any dependencies
                                if (typeof handlerInitFn == "function") {
                                    ko.dependencyDetection.ignore(function() {
                                        var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);

                                        // If this binding handler claims to control descendant bindings, make a note of this
                                        if (initResult && initResult['controlsDescendantBindings']) {
                                            if (bindingHandlerThatControlsDescendantBindings !== undefined)
                                                throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
                                            bindingHandlerThatControlsDescendantBindings = bindingKey;
                                        }
                                    });
                                }

                                // Run update in its own computed wrapper
                                if (typeof handlerUpdateFn == "function") {
                                    ko.dependentObservable(
                                        function() {
                                            handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
                                        },
                                        null,
                                        { disposeWhenNodeIsRemoved: node }
                                    );
                                }
                            } catch (ex) {
                                ex.message = "Unable to process binding \"" + bindingKey + ": " + bindings[bindingKey] + "\"\nMessage: " + ex.message;
                                throw ex;
                            }
                        });
                    }

                    var shouldBindDescendants = bindingHandlerThatControlsDescendantBindings === undefined;
                    return {
                        'shouldBindDescendants': shouldBindDescendants,
                        'bindingContextForDescendants': shouldBindDescendants && contextToExtend
                    };
                };

                ko.storedBindingContextForNode = function (node) {
                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
                    return bindingInfo && bindingInfo.context;
                }

                function getBindingContext(viewModelOrBindingContext, extendContextCallback) {
                    return viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
                        ? viewModelOrBindingContext
                        : new ko.bindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
                }

                ko.applyBindingAccessorsToNode = function (node, bindings, viewModelOrBindingContext) {
                    if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
                        ko.virtualElements.normaliseVirtualElementDomStructure(node);
                    return applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));
                };

                ko.applyBindingsToNode = function (node, bindings, viewModelOrBindingContext) {
                    var context = getBindingContext(viewModelOrBindingContext);
                    return ko.applyBindingAccessorsToNode(node, makeBindingAccessors(bindings, context, node), context);
                };

                ko.applyBindingsToDescendants = function(viewModelOrBindingContext, rootNode) {
                    if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
                        applyBindingsToDescendantsInternal(getBindingContext(viewModelOrBindingContext), rootNode);
                };

                ko.applyBindings = function (viewModelOrBindingContext, rootNode, extendContextCallback) {
                    // If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.
                    if (!jQueryInstance && window['jQuery']) {
                        jQueryInstance = window['jQuery'];
                    }

                    if (arguments.length < 2) {
                        rootNode = document.body;
                        if (!rootNode) {
                            throw Error("ko.applyBindings: could not find document.body; has the document been loaded?");
                        }
                    } else if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {
                        throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
                    }

                    applyBindingsToNodeAndDescendantsInternal(getBindingContext(viewModelOrBindingContext, extendContextCallback), rootNode);
                };

                // Retrieving binding context from arbitrary nodes
                ko.contextFor = function(node) {
                    // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
                    if (node && (node.nodeType === 1 || node.nodeType === 8)) {
                        return ko.storedBindingContextForNode(node);
                    }
                    return undefined;
                };
                ko.dataFor = function(node) {
                    var context = ko.contextFor(node);
                    return context ? context['$data'] : undefined;
                };

                ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
                ko.exportSymbol('bindingEvent', ko.bindingEvent);
                ko.exportSymbol('bindingEvent.subscribe', ko.bindingEvent.subscribe);
                ko.exportSymbol('bindingEvent.startPossiblyAsyncContentBinding', ko.bindingEvent.startPossiblyAsyncContentBinding);
                ko.exportSymbol('applyBindings', ko.applyBindings);
                ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
                ko.exportSymbol('applyBindingAccessorsToNode', ko.applyBindingAccessorsToNode);
                ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
                ko.exportSymbol('contextFor', ko.contextFor);
                ko.exportSymbol('dataFor', ko.dataFor);
            })();
            (function(undefined) {
                var loadingSubscribablesCache = {}, // Tracks component loads that are currently in flight
                    loadedDefinitionsCache = {};    // Tracks component loads that have already completed

                ko.components = {
                    get: function(componentName, callback) {
                        var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);
                        if (cachedDefinition) {
                            // It's already loaded and cached. Reuse the same definition object.
                            // Note that for API consistency, even cache hits complete asynchronously by default.
                            // You can bypass this by putting synchronous:true on your component config.
                            if (cachedDefinition.isSynchronousComponent) {
                                ko.dependencyDetection.ignore(function() { // See comment in loaderRegistryBehaviors.js for reasoning
                                    callback(cachedDefinition.definition);
                                });
                            } else {
                                ko.tasks.schedule(function() { callback(cachedDefinition.definition); });
                            }
                        } else {
                            // Join the loading process that is already underway, or start a new one.
                            loadComponentAndNotify(componentName, callback);
                        }
                    },

                    clearCachedDefinition: function(componentName) {
                        delete loadedDefinitionsCache[componentName];
                    },

                    _getFirstResultFromLoaders: getFirstResultFromLoaders
                };

                function getObjectOwnProperty(obj, propName) {
                    return Object.prototype.hasOwnProperty.call(obj, propName) ? obj[propName] : undefined;
                }

                function loadComponentAndNotify(componentName, callback) {
                    var subscribable = getObjectOwnProperty(loadingSubscribablesCache, componentName),
                        completedAsync;
                    if (!subscribable) {
                        // It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.
                        subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();
                        subscribable.subscribe(callback);

                        beginLoadingComponent(componentName, function(definition, config) {
                            var isSynchronousComponent = !!(config && config['synchronous']);
                            loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };
                            delete loadingSubscribablesCache[componentName];

                            // For API consistency, all loads complete asynchronously. However we want to avoid
                            // adding an extra task schedule if it's unnecessary (i.e., the completion is already
                            // async).
                            //
                            // You can bypass the 'always asynchronous' feature by putting the synchronous:true
                            // flag on your component configuration when you register it.
                            if (completedAsync || isSynchronousComponent) {
                                // Note that notifySubscribers ignores any dependencies read within the callback.
                                // See comment in loaderRegistryBehaviors.js for reasoning
                                subscribable['notifySubscribers'](definition);
                            } else {
                                ko.tasks.schedule(function() {
                                    subscribable['notifySubscribers'](definition);
                                });
                            }
                        });
                        completedAsync = true;
                    } else {
                        subscribable.subscribe(callback);
                    }
                }

                function beginLoadingComponent(componentName, callback) {
                    getFirstResultFromLoaders('getConfig', [componentName], function(config) {
                        if (config) {
                            // We have a config, so now load its definition
                            getFirstResultFromLoaders('loadComponent', [componentName, config], function(definition) {
                                callback(definition, config);
                            });
                        } else {
                            // The component has no config - it's unknown to all the loaders.
                            // Note that this is not an error (e.g., a module loading error) - that would abort the
                            // process and this callback would not run. For this callback to run, all loaders must
                            // have confirmed they don't know about this component.
                            callback(null, null);
                        }
                    });
                }

                function getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders) {
                    // On the first call in the stack, start with the full set of loaders
                    if (!candidateLoaders) {
                        candidateLoaders = ko.components['loaders'].slice(0); // Use a copy, because we'll be mutating this array
                    }

                    // Try the next candidate
                    var currentCandidateLoader = candidateLoaders.shift();
                    if (currentCandidateLoader) {
                        var methodInstance = currentCandidateLoader[methodName];
                        if (methodInstance) {
                            var wasAborted = false,
                                synchronousReturnValue = methodInstance.apply(currentCandidateLoader, argsExceptCallback.concat(function(result) {
                                    if (wasAborted) {
                                        callback(null);
                                    } else if (result !== null) {
                                        // This candidate returned a value. Use it.
                                        callback(result);
                                    } else {
                                        // Try the next candidate
                                        getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
                                    }
                                }));

                            // Currently, loaders may not return anything synchronously. This leaves open the possibility
                            // that we'll extend the API to support synchronous return values in the future. It won't be
                            // a breaking change, because currently no loader is allowed to return anything except undefined.
                            if (synchronousReturnValue !== undefined) {
                                wasAborted = true;

                                // Method to suppress exceptions will remain undocumented. This is only to keep
                                // KO's specs running tidily, since we can observe the loading got aborted without
                                // having exceptions cluttering up the console too.
                                if (!currentCandidateLoader['suppressLoaderExceptions']) {
                                    throw new Error('Component loaders must supply values by invoking the callback, not by returning values synchronously.');
                                }
                            }
                        } else {
                            // This candidate doesn't have the relevant handler. Synchronously move on to the next one.
                            getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
                        }
                    } else {
                        // No candidates returned a value
                        callback(null);
                    }
                }

                // Reference the loaders via string name so it's possible for developers
                // to replace the whole array by assigning to ko.components.loaders
                ko.components['loaders'] = [];

                ko.exportSymbol('components', ko.components);
                ko.exportSymbol('components.get', ko.components.get);
                ko.exportSymbol('components.clearCachedDefinition', ko.components.clearCachedDefinition);
            })();
            (function(undefined) {

                // The default loader is responsible for two things:
                // 1. Maintaining the default in-memory registry of component configuration objects
                //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
                // 2. Answering requests for components by fetching configuration objects
                //    from that default in-memory registry and resolving them into standard
                //    component definition objects (of the form { createViewModel: ..., template: ... })
                // Custom loaders may override either of these facilities, i.e.,
                // 1. To supply configuration objects from some other source (e.g., conventions)
                // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.

                var defaultConfigRegistry = {};

                ko.components.register = function(componentName, config) {
                    if (!config) {
                        throw new Error('Invalid configuration for ' + componentName);
                    }

                    if (ko.components.isRegistered(componentName)) {
                        throw new Error('Component ' + componentName + ' is already registered');
                    }

                    defaultConfigRegistry[componentName] = config;
                };

                ko.components.isRegistered = function(componentName) {
                    return Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);
                };

                ko.components.unregister = function(componentName) {
                    delete defaultConfigRegistry[componentName];
                    ko.components.clearCachedDefinition(componentName);
                };

                ko.components.defaultLoader = {
                    'getConfig': function(componentName, callback) {
                        var result = ko.components.isRegistered(componentName)
                            ? defaultConfigRegistry[componentName]
                            : null;
                        callback(result);
                    },

                    'loadComponent': function(componentName, config, callback) {
                        var errorCallback = makeErrorCallback(componentName);
                        possiblyGetConfigFromAmd(errorCallback, config, function(loadedConfig) {
                            resolveConfig(componentName, errorCallback, loadedConfig, callback);
                        });
                    },

                    'loadTemplate': function(componentName, templateConfig, callback) {
                        resolveTemplate(makeErrorCallback(componentName), templateConfig, callback);
                    },

                    'loadViewModel': function(componentName, viewModelConfig, callback) {
                        resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback);
                    }
                };

                var createViewModelKey = 'createViewModel';

                // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
                // into the standard component definition format:
                //    { template: <ArrayOfDomNodes>, createViewModel: function(params, componentInfo) { ... } }.
                // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
                // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
                // so this is implemented manually below.
                function resolveConfig(componentName, errorCallback, config, callback) {
                    var result = {},
                        makeCallBackWhenZero = 2,
                        tryIssueCallback = function() {
                            if (--makeCallBackWhenZero === 0) {
                                callback(result);
                            }
                        },
                        templateConfig = config['template'],
                        viewModelConfig = config['viewModel'];

                    if (templateConfig) {
                        possiblyGetConfigFromAmd(errorCallback, templateConfig, function(loadedConfig) {
                            ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], function(resolvedTemplate) {
                                result['template'] = resolvedTemplate;
                                tryIssueCallback();
                            });
                        });
                    } else {
                        tryIssueCallback();
                    }

                    if (viewModelConfig) {
                        possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function(loadedConfig) {
                            ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], function(resolvedViewModel) {
                                result[createViewModelKey] = resolvedViewModel;
                                tryIssueCallback();
                            });
                        });
                    } else {
                        tryIssueCallback();
                    }
                }

                function resolveTemplate(errorCallback, templateConfig, callback) {
                    if (typeof templateConfig === 'string') {
                        // Markup - parse it
                        callback(ko.utils.parseHtmlFragment(templateConfig));
                    } else if (templateConfig instanceof Array) {
                        // Assume already an array of DOM nodes - pass through unchanged
                        callback(templateConfig);
                    } else if (isDocumentFragment(templateConfig)) {
                        // Document fragment - use its child nodes
                        callback(ko.utils.makeArray(templateConfig.childNodes));
                    } else if (templateConfig['element']) {
                        var element = templateConfig['element'];
                        if (isDomElement(element)) {
                            // Element instance - copy its child nodes
                            callback(cloneNodesFromTemplateSourceElement(element));
                        } else if (typeof element === 'string') {
                            // Element ID - find it, then copy its child nodes
                            var elemInstance = document.getElementById(element);
                            if (elemInstance) {
                                callback(cloneNodesFromTemplateSourceElement(elemInstance));
                            } else {
                                errorCallback('Cannot find element with ID ' + element);
                            }
                        } else {
                            errorCallback('Unknown element type: ' + element);
                        }
                    } else {
                        errorCallback('Unknown template value: ' + templateConfig);
                    }
                }

                function resolveViewModel(errorCallback, viewModelConfig, callback) {
                    if (typeof viewModelConfig === 'function') {
                        // Constructor - convert to standard factory function format
                        // By design, this does *not* supply componentInfo to the constructor, as the intent is that
                        // componentInfo contains non-viewmodel data (e.g., the component's element) that should only
                        // be used in factory functions, not viewmodel constructors.
                        callback(function (params /*, componentInfo */) {
                            return new viewModelConfig(params);
                        });
                    } else if (typeof viewModelConfig[createViewModelKey] === 'function') {
                        // Already a factory function - use it as-is
                        callback(viewModelConfig[createViewModelKey]);
                    } else if ('instance' in viewModelConfig) {
                        // Fixed object instance - promote to createViewModel format for API consistency
                        var fixedInstance = viewModelConfig['instance'];
                        callback(function (params, componentInfo) {
                            return fixedInstance;
                        });
                    } else if ('viewModel' in viewModelConfig) {
                        // Resolved AMD module whose value is of the form { viewModel: ... }
                        resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);
                    } else {
                        errorCallback('Unknown viewModel value: ' + viewModelConfig);
                    }
                }

                function cloneNodesFromTemplateSourceElement(elemInstance) {
                    switch (ko.utils.tagNameLower(elemInstance)) {
                        case 'script':
                            return ko.utils.parseHtmlFragment(elemInstance.text);
                        case 'textarea':
                            return ko.utils.parseHtmlFragment(elemInstance.value);
                        case 'template':
                            // For browsers with proper <template> element support (i.e., where the .content property
                            // gives a document fragment), use that document fragment.
                            if (isDocumentFragment(elemInstance.content)) {
                                return ko.utils.cloneNodes(elemInstance.content.childNodes);
                            }
                    }

                    // Regular elements such as <div>, and <template> elements on old browsers that don't really
                    // understand <template> and just treat it as a regular container
                    return ko.utils.cloneNodes(elemInstance.childNodes);
                }

                function isDomElement(obj) {
                    if (window['HTMLElement']) {
                        return obj instanceof HTMLElement;
                    } else {
                        return obj && obj.tagName && obj.nodeType === 1;
                    }
                }

                function isDocumentFragment(obj) {
                    if (window['DocumentFragment']) {
                        return obj instanceof DocumentFragment;
                    } else {
                        return obj && obj.nodeType === 11;
                    }
                }

                function possiblyGetConfigFromAmd(errorCallback, config, callback) {
                    if (typeof config['require'] === 'string') {
                        // The config is the value of an AMD module
                        if (amdRequire || window['require']) {
                            (amdRequire || window['require'])([config['require']], function (module) {
                                if (module && typeof module === 'object' && module.__esModule && module.default) {
                                    module = module.default;
                                }
                                callback(module);
                            });
                        } else {
                            errorCallback('Uses require, but no AMD loader is present');
                        }
                    } else {
                        callback(config);
                    }
                }

                function makeErrorCallback(componentName) {
                    return function (message) {
                        throw new Error('Component \'' + componentName + '\': ' + message);
                    };
                }

                ko.exportSymbol('components.register', ko.components.register);
                ko.exportSymbol('components.isRegistered', ko.components.isRegistered);
                ko.exportSymbol('components.unregister', ko.components.unregister);

                // Expose the default loader so that developers can directly ask it for configuration
                // or to resolve configuration
                ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);

                // By default, the default loader is the only registered component loader
                ko.components['loaders'].push(ko.components.defaultLoader);

                // Privately expose the underlying config registry for use in old-IE shim
                ko.components._allRegisteredComponents = defaultConfigRegistry;
            })();
            (function (undefined) {
                // Overridable API for determining which component name applies to a given node. By overriding this,
                // you can for example map specific tagNames to components that are not preregistered.
                ko.components['getComponentNameForNode'] = function(node) {
                    var tagNameLower = ko.utils.tagNameLower(node);
                    if (ko.components.isRegistered(tagNameLower)) {
                        // Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
                        if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {
                            return tagNameLower;
                        }
                    }
                };

                ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {
                    // Determine if it's really a custom element matching a component
                    if (node.nodeType === 1) {
                        var componentName = ko.components['getComponentNameForNode'](node);
                        if (componentName) {
                            // It does represent a component, so add a component binding for it
                            allBindings = allBindings || {};

                            if (allBindings['component']) {
                                // Avoid silently overwriting some other 'component' binding that may already be on the element
                                throw new Error('Cannot use the "component" binding on a custom element matching a component');
                            }

                            var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };

                            allBindings['component'] = valueAccessors
                                ? function() { return componentBindingValue; }
                                : componentBindingValue;
                        }
                    }

                    return allBindings;
                }

                var nativeBindingProviderInstance = new ko.bindingProvider();

                function getComponentParamsFromCustomElement(elem, bindingContext) {
                    var paramsAttribute = elem.getAttribute('params');

                    if (paramsAttribute) {
                        var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
                            rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {
                                return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
                            }),
                            result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
                                var paramValue = paramValueComputed.peek();
                                // Does the evaluation of the parameter value unwrap any observables?
                                if (!paramValueComputed.isActive()) {
                                    // No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.
                                    // Example: "someVal: firstName, age: 123" (whether or not firstName is an observable/computed)
                                    return paramValue;
                                } else {
                                    // Yes it does. Supply a computed property that unwraps both the outer (binding expression)
                                    // level of observability, and any inner (resulting model value) level of observability.
                                    // This means the component doesn't have to worry about multiple unwrapping. If the value is a
                                    // writable observable, the computed will also be writable and pass the value on to the observable.
                                    return ko.computed({
                                        'read': function() {
                                            return ko.utils.unwrapObservable(paramValueComputed());
                                        },
                                        'write': ko.isWriteableObservable(paramValue) && function(value) {
                                            paramValueComputed()(value);
                                        },
                                        disposeWhenNodeIsRemoved: elem
                                    });
                                }
                            });

                        // Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'
                        // This is in case the developer wants to react to outer (binding) observability separately from inner
                        // (model value) observability, or in case the model value observable has subobservables.
                        if (!Object.prototype.hasOwnProperty.call(result, '$raw')) {
                            result['$raw'] = rawParamComputedValues;
                        }

                        return result;
                    } else {
                        // For consistency, absence of a "params" attribute is treated the same as the presence of
                        // any empty one. Otherwise component viewmodels need special code to check whether or not
                        // 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
                        return { '$raw': {} };
                    }
                }

                // --------------------------------------------------------------------------------
                // Compatibility code for older (pre-HTML5) IE browsers

                if (ko.utils.ieVersion < 9) {
                    // Whenever you preregister a component, enable it as a custom element in the current document
                    ko.components['register'] = (function(originalFunction) {
                        return function(componentName) {
                            document.createElement(componentName); // Allows IE<9 to parse markup containing the custom element
                            return originalFunction.apply(this, arguments);
                        }
                    })(ko.components['register']);

                    // Whenever you create a document fragment, enable all preregistered component names as custom elements
                    // This is needed to make innerShiv/jQuery HTML parsing correctly handle the custom elements
                    document.createDocumentFragment = (function(originalFunction) {
                        return function() {
                            var newDocFrag = originalFunction(),
                                allComponents = ko.components._allRegisteredComponents;
                            for (var componentName in allComponents) {
                                if (Object.prototype.hasOwnProperty.call(allComponents, componentName)) {
                                    newDocFrag.createElement(componentName);
                                }
                            }
                            return newDocFrag;
                        };
                    })(document.createDocumentFragment);
                }
            })();(function(undefined) {
                var componentLoadingOperationUniqueId = 0;

                ko.bindingHandlers['component'] = {
                    'init': function(element, valueAccessor, ignored1, ignored2, bindingContext) {
                        var currentViewModel,
                            currentLoadingOperationId,
                            afterRenderSub,
                            disposeAssociatedComponentViewModel = function () {
                                var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];
                                if (typeof currentViewModelDispose === 'function') {
                                    currentViewModelDispose.call(currentViewModel);
                                }
                                if (afterRenderSub) {
                                    afterRenderSub.dispose();
                                }
                                afterRenderSub = null;
                                currentViewModel = null;
                                // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
                                currentLoadingOperationId = null;
                            },
                            originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));

                        ko.virtualElements.emptyNode(element);
                        ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);

                        ko.computed(function () {
                            var value = ko.utils.unwrapObservable(valueAccessor()),
                                componentName, componentParams;

                            if (typeof value === 'string') {
                                componentName = value;
                            } else {
                                componentName = ko.utils.unwrapObservable(value['name']);
                                componentParams = ko.utils.unwrapObservable(value['params']);
                            }

                            if (!componentName) {
                                throw new Error('No component name specified');
                            }

                            var asyncContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);

                            var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;
                            ko.components.get(componentName, function(componentDefinition) {
                                // If this is not the current load operation for this element, ignore it.
                                if (currentLoadingOperationId !== loadingOperationId) {
                                    return;
                                }

                                // Clean up previous state
                                disposeAssociatedComponentViewModel();

                                // Instantiate and bind new component. Implicitly this cleans any old DOM nodes.
                                if (!componentDefinition) {
                                    throw new Error('Unknown component \'' + componentName + '\'');
                                }
                                cloneTemplateIntoElement(componentName, componentDefinition, element);

                                var componentInfo = {
                                    'element': element,
                                    'templateNodes': originalChildNodes
                                };

                                var componentViewModel = createViewModel(componentDefinition, componentParams, componentInfo),
                                    childBindingContext = asyncContext['createChildContext'](componentViewModel, {
                                        'extend': function(ctx) {
                                            ctx['$component'] = componentViewModel;
                                            ctx['$componentTemplateNodes'] = originalChildNodes;
                                        }
                                    });

                                if (componentViewModel && componentViewModel['koDescendantsComplete']) {
                                    afterRenderSub = ko.bindingEvent.subscribe(element, ko.bindingEvent.descendantsComplete, componentViewModel['koDescendantsComplete'], componentViewModel);
                                }

                                currentViewModel = componentViewModel;
                                ko.applyBindingsToDescendants(childBindingContext, element);
                            });
                        }, null, { disposeWhenNodeIsRemoved: element });

                        return { 'controlsDescendantBindings': true };
                    }
                };

                ko.virtualElements.allowedBindings['component'] = true;

                function cloneTemplateIntoElement(componentName, componentDefinition, element) {
                    var template = componentDefinition['template'];
                    if (!template) {
                        throw new Error('Component \'' + componentName + '\' has no template');
                    }

                    var clonedNodesArray = ko.utils.cloneNodes(template);
                    ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);
                }

                function createViewModel(componentDefinition, componentParams, componentInfo) {
                    var componentViewModelFactory = componentDefinition['createViewModel'];
                    return componentViewModelFactory
                        ? componentViewModelFactory.call(componentDefinition, componentParams, componentInfo)
                        : componentParams; // Template-only component
                }

            })();
            var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };
            ko.bindingHandlers['attr'] = {
                'update': function(element, valueAccessor, allBindings) {
                    var value = ko.utils.unwrapObservable(valueAccessor()) || {};
                    ko.utils.objectForEach(value, function(attrName, attrValue) {
                        attrValue = ko.utils.unwrapObservable(attrValue);

                        // Find the namespace of this attribute, if any.
                        var prefixLen = attrName.indexOf(':');
                        var namespace = "lookupNamespaceURI" in element && prefixLen > 0 && element.lookupNamespaceURI(attrName.substr(0, prefixLen));

                        // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
                        // when someProp is a "no value"-like value (strictly null, false, or undefined)
                        // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
                        var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
                        if (toRemove) {
                            namespace ? element.removeAttributeNS(namespace, attrName) : element.removeAttribute(attrName);
                        } else {
                            attrValue = attrValue.toString();
                        }

                        // In IE <= 7 and IE8 Quirks Mode, you have to use the JavaScript property name instead of the
                        // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
                        // but instead of figuring out the mode, we'll just set the attribute through the JavaScript
                        // property for IE <= 8.
                        if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavaScriptMap) {
                            attrName = attrHtmlToJavaScriptMap[attrName];
                            if (toRemove)
                                element.removeAttribute(attrName);
                            else
                                element[attrName] = attrValue;
                        } else if (!toRemove) {
                            namespace ? element.setAttributeNS(namespace, attrName, attrValue) : element.setAttribute(attrName, attrValue);
                        }

                        // Treat "name" specially - although you can think of it as an attribute, it also needs
                        // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
                        // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
                        // entirely, and there's no strong reason to allow for such casing in HTML.
                        if (attrName === "name") {
                            ko.utils.setElementName(element, toRemove ? "" : attrValue);
                        }
                    });
                }
            };
            (function() {

                ko.bindingHandlers['checked'] = {
                    'after': ['value', 'attr'],
                    'init': function (element, valueAccessor, allBindings) {
                        var checkedValue = ko.pureComputed(function() {
                            // Treat "value" like "checkedValue" when it is included with "checked" binding
                            if (allBindings['has']('checkedValue')) {
                                return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
                            } else if (useElementValue) {
                                if (allBindings['has']('value')) {
                                    return ko.utils.unwrapObservable(allBindings.get('value'));
                                } else {
                                    return element.value;
                                }
                            }
                        });

                        function updateModel() {
                            // This updates the model value from the view value.
                            // It runs in response to DOM events (click) and changes in checkedValue.
                            var isChecked = element.checked,
                                elemValue = checkedValue();

                            // When we're first setting up this computed, don't change any model state.
                            if (ko.computedContext.isInitial()) {
                                return;
                            }

                            // We can ignore unchecked radio buttons, because some other radio
                            // button will be checked, and that one can take care of updating state.
                            // Also ignore value changes to an already unchecked checkbox.
                            if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {
                                return;
                            }

                            var modelValue = ko.dependencyDetection.ignore(valueAccessor);
                            if (valueIsArray) {
                                var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,
                                    saveOldValue = oldElemValue;
                                oldElemValue = elemValue;

                                if (saveOldValue !== elemValue) {
                                    // When we're responding to the checkedValue changing, and the element is
                                    // currently checked, replace the old elem value with the new elem value
                                    // in the model array.
                                    if (isChecked) {
                                        ko.utils.addOrRemoveItem(writableValue, elemValue, true);
                                        ko.utils.addOrRemoveItem(writableValue, saveOldValue, false);
                                    }
                                } else {
                                    // When we're responding to the user having checked/unchecked a checkbox,
                                    // add/remove the element value to the model array.
                                    ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);
                                }

                                if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
                                    modelValue(writableValue);
                                }
                            } else {
                                if (isCheckbox) {
                                    if (elemValue === undefined) {
                                        elemValue = isChecked;
                                    } else if (!isChecked) {
                                        elemValue = undefined;
                                    }
                                }
                                ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
                            }
                        };

                        function updateView() {
                            // This updates the view value from the model value.
                            // It runs in response to changes in the bound (checked) value.
                            var modelValue = ko.utils.unwrapObservable(valueAccessor()),
                                elemValue = checkedValue();

                            if (valueIsArray) {
                                // When a checkbox is bound to an array, being checked represents its value being present in that array
                                element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
                                oldElemValue = elemValue;
                            } else if (isCheckbox && elemValue === undefined) {
                                // When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
                                // being checked represents the value being trueish
                                element.checked = !!modelValue;
                            } else {
                                // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
                                element.checked = (checkedValue() === modelValue);
                            }
                        };

                        var isCheckbox = element.type == "checkbox",
                            isRadio = element.type == "radio";

                        // Only bind to check boxes and radio buttons
                        if (!isCheckbox && !isRadio) {
                            return;
                        }

                        var rawValue = valueAccessor(),
                            valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
                            rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
                            useElementValue = isRadio || valueIsArray,
                            oldElemValue = valueIsArray ? checkedValue() : undefined;

                        // IE 6 won't allow radio buttons to be selected unless they have a name
                        if (isRadio && !element.name)
                            ko.bindingHandlers['uniqueName']['init'](element, function() { return true });

                        // Set up two computeds to update the binding:

                        // The first responds to changes in the checkedValue value and to element clicks
                        ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });
                        ko.utils.registerEventHandler(element, "click", updateModel);

                        // The second responds to changes in the model value (the one associated with the checked binding)
                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });

                        rawValue = undefined;
                    }
                };
                ko.expressionRewriting.twoWayBindings['checked'] = true;

                ko.bindingHandlers['checkedValue'] = {
                    'update': function (element, valueAccessor) {
                        element.value = ko.utils.unwrapObservable(valueAccessor());
                    }
                };

            })();var classesWrittenByBindingKey = '__ko__cssValue';
            ko.bindingHandlers['class'] = {
                'update': function (element, valueAccessor) {
                    var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));
                    ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
                    element[classesWrittenByBindingKey] = value;
                    ko.utils.toggleDomNodeCssClass(element, value, true);
                }
            };

            ko.bindingHandlers['css'] = {
                'update': function (element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor());
                    if (value !== null && typeof value == "object") {
                        ko.utils.objectForEach(value, function(className, shouldHaveClass) {
                            shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
                            ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
                        });
                    } else {
                        ko.bindingHandlers['class']['update'](element, valueAccessor);
                    }
                }
            };
            ko.bindingHandlers['enable'] = {
                'update': function (element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor());
                    if (value && element.disabled)
                        element.removeAttribute("disabled");
                    else if ((!value) && (!element.disabled))
                        element.disabled = true;
                }
            };

            ko.bindingHandlers['disable'] = {
                'update': function (element, valueAccessor) {
                    ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
                }
            };
// For certain common events (currently just 'click'), allow a simplified data-binding syntax
// e.g. click:handler instead of the usual full-length event:{click:handler}
            function makeEventHandlerShortcut(eventName) {
                ko.bindingHandlers[eventName] = {
                    'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                        var newValueAccessor = function () {
                            var result = {};
                            result[eventName] = valueAccessor();
                            return result;
                        };
                        return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindings, viewModel, bindingContext);
                    }
                }
            }

            ko.bindingHandlers['event'] = {
                'init' : function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                    var eventsToHandle = valueAccessor() || {};
                    ko.utils.objectForEach(eventsToHandle, function(eventName) {
                        if (typeof eventName == "string") {
                            ko.utils.registerEventHandler(element, eventName, function (event) {
                                var handlerReturnValue;
                                var handlerFunction = valueAccessor()[eventName];
                                if (!handlerFunction)
                                    return;

                                try {
                                    // Take all the event args, and prefix with the viewmodel
                                    var argsForHandler = ko.utils.makeArray(arguments);
                                    viewModel = bindingContext['$data'];
                                    argsForHandler.unshift(viewModel);
                                    handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
                                } finally {
                                    if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
                                        if (event.preventDefault)
                                            event.preventDefault();
                                        else
                                            event.returnValue = false;
                                    }
                                }

                                var bubble = allBindings.get(eventName + 'Bubble') !== false;
                                if (!bubble) {
                                    event.cancelBubble = true;
                                    if (event.stopPropagation)
                                        event.stopPropagation();
                                }
                            });
                        }
                    });
                }
            };
// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
            ko.bindingHandlers['foreach'] = {
                makeTemplateValueAccessor: function(valueAccessor) {
                    return function() {
                        var modelValue = valueAccessor(),
                            unwrappedValue = ko.utils.peekObservable(modelValue);    // Unwrap without setting a dependency here

                        // If unwrappedValue is the array, pass in the wrapped value on its own
                        // The value will be unwrapped and tracked within the template binding
                        // (See https://github.com/SteveSanderson/knockout/issues/523)
                        if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
                            return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };

                        // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
                        ko.utils.unwrapObservable(modelValue);
                        return {
                            'foreach': unwrappedValue['data'],
                            'as': unwrappedValue['as'],
                            'noChildContext': unwrappedValue['noChildContext'],
                            'includeDestroyed': unwrappedValue['includeDestroyed'],
                            'afterAdd': unwrappedValue['afterAdd'],
                            'beforeRemove': unwrappedValue['beforeRemove'],
                            'afterRender': unwrappedValue['afterRender'],
                            'beforeMove': unwrappedValue['beforeMove'],
                            'afterMove': unwrappedValue['afterMove'],
                            'templateEngine': ko.nativeTemplateEngine.instance
                        };
                    };
                },
                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                    return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
                },
                'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                    return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);
                }
            };
            ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
            ko.virtualElements.allowedBindings['foreach'] = true;
            var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
            var hasfocusLastValue = '__ko_hasfocusLastValue';
            ko.bindingHandlers['hasfocus'] = {
                'init': function(element, valueAccessor, allBindings) {
                    var handleElementFocusChange = function(isFocused) {
                        // Where possible, ignore which event was raised and determine focus state using activeElement,
                        // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
                        // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
                        // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
                        // from calling 'blur()' on the element when it loses focus.
                        // Discussion at https://github.com/SteveSanderson/knockout/pull/352
                        element[hasfocusUpdatingProperty] = true;
                        var ownerDoc = element.ownerDocument;
                        if ("activeElement" in ownerDoc) {
                            var active;
                            try {
                                active = ownerDoc.activeElement;
                            } catch(e) {
                                // IE9 throws if you access activeElement during page load (see issue #703)
                                active = ownerDoc.body;
                            }
                            isFocused = (active === element);
                        }
                        var modelValue = valueAccessor();
                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);

                        //cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function
                        element[hasfocusLastValue] = isFocused;
                        element[hasfocusUpdatingProperty] = false;
                    };
                    var handleElementFocusIn = handleElementFocusChange.bind(null, true);
                    var handleElementFocusOut = handleElementFocusChange.bind(null, false);

                    ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
                    ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
                    ko.utils.registerEventHandler(element, "blur",  handleElementFocusOut);
                    ko.utils.registerEventHandler(element, "focusout",  handleElementFocusOut); // For IE

                    // Assume element is not focused (prevents "blur" being called initially)
                    element[hasfocusLastValue] = false;
                },
                'update': function(element, valueAccessor) {
                    var value = !!ko.utils.unwrapObservable(valueAccessor());

                    if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {
                        value ? element.focus() : element.blur();

                        // In IE, the blur method doesn't always cause the element to lose focus (for example, if the window is not in focus).
                        // Setting focus to the body element does seem to be reliable in IE, but should only be used if we know that the current
                        // element was focused already.
                        if (!value && element[hasfocusLastValue]) {
                            element.ownerDocument.body.focus();
                        }

                        // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
                        ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]);
                    }
                }
            };
            ko.expressionRewriting.twoWayBindings['hasfocus'] = true;

            ko.bindingHandlers['hasFocus'] = ko.bindingHandlers['hasfocus']; // Make "hasFocus" an alias
            ko.expressionRewriting.twoWayBindings['hasFocus'] = 'hasfocus';
            ko.bindingHandlers['html'] = {
                'init': function() {
                    // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
                    return { 'controlsDescendantBindings': true };
                },
                'update': function (element, valueAccessor) {
                    // setHtml will unwrap the value if needed
                    ko.utils.setHtml(element, valueAccessor());
                }
            };
            (function () {

// Makes a binding like with or if
                function makeWithIfBinding(bindingKey, isWith, isNot) {
                    ko.bindingHandlers[bindingKey] = {
                        'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                            var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, completeOnRender, needAsyncContext, renderOnEveryChange;

                            if (isWith) {
                                var as = allBindings.get('as'), noChildContext = allBindings.get('noChildContext');
                                renderOnEveryChange = !(as && noChildContext);
                                contextOptions = { 'as': as, 'noChildContext': noChildContext, 'exportDependencies': renderOnEveryChange };
                            }

                            completeOnRender = allBindings.get("completeOn") == "render";
                            needAsyncContext = completeOnRender || allBindings['has'](ko.bindingEvent.descendantsComplete);

                            ko.computed(function() {
                                var value = ko.utils.unwrapObservable(valueAccessor()),
                                    shouldDisplay = !isNot !== !value, // equivalent to isNot ? !value : !!value,
                                    isInitial = !savedNodes,
                                    childContext;

                                if (!renderOnEveryChange && shouldDisplay === didDisplayOnLastUpdate) {
                                    return;
                                }

                                if (needAsyncContext) {
                                    bindingContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
                                }

                                if (shouldDisplay) {
                                    if (!isWith || renderOnEveryChange) {
                                        contextOptions['dataDependency'] = ko.computedContext.computed();
                                    }

                                    if (isWith) {
                                        childContext = bindingContext['createChildContext'](typeof value == "function" ? value : valueAccessor, contextOptions);
                                    } else if (ko.computedContext.getDependenciesCount()) {
                                        childContext = bindingContext['extend'](null, contextOptions);
                                    } else {
                                        childContext = bindingContext;
                                    }
                                }

                                // Save a copy of the inner nodes on the initial update, but only if we have dependencies.
                                if (isInitial && ko.computedContext.getDependenciesCount()) {
                                    savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
                                }

                                if (shouldDisplay) {
                                    if (!isInitial) {
                                        ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(savedNodes));
                                    }

                                    ko.applyBindingsToDescendants(childContext, element);
                                } else {
                                    ko.virtualElements.emptyNode(element);

                                    if (!completeOnRender) {
                                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
                                    }
                                }

                                didDisplayOnLastUpdate = shouldDisplay;

                            }, null, { disposeWhenNodeIsRemoved: element });

                            return { 'controlsDescendantBindings': true };
                        }
                    };
                    ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
                    ko.virtualElements.allowedBindings[bindingKey] = true;
                }

// Construct the actual binding handlers
                makeWithIfBinding('if');
                makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
                makeWithIfBinding('with', true /* isWith */);

            })();ko.bindingHandlers['let'] = {
                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                    // Make a modified binding context, with extra properties, and apply it to descendant elements
                    var innerContext = bindingContext['extend'](valueAccessor);
                    ko.applyBindingsToDescendants(innerContext, element);

                    return { 'controlsDescendantBindings': true };
                }
            };
            ko.virtualElements.allowedBindings['let'] = true;
            var captionPlaceholder = {};
            ko.bindingHandlers['options'] = {
                '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);
                    }

                    // Ensures that the binding processor doesn't try to bind the options
                    return { 'controlsDescendantBindings': true };
                },
                'update': function (element, valueAccessor, allBindings) {
                    function selectedOptions() {
                        return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });
                    }

                    var selectWasPreviouslyEmpty = element.length == 0,
                        multiple = element.multiple,
                        previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? element.scrollTop : null,
                        unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),
                        valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings['has']('value'),
                        includeDestroyed = allBindings.get('optionsIncludeDestroyed'),
                        arrayToDomNodeChildrenOptions = {},
                        captionValue,
                        filteredArray,
                        previousSelectedValues = [];

                    if (!valueAllowUnset) {
                        if (multiple) {
                            previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
                        } else if (element.selectedIndex >= 0) {
                            previousSelectedValues.push(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) {
                            return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
                        });

                        // 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 (captionValue !== null && captionValue !== undefined) {
                                filteredArray.unshift(captionPlaceholder);
                            }
                        }
                    } else {
                        // If a falsy value is provided (e.g. null), we'll simply empty the select element
                    }

                    function applyToObject(object, predicate, defaultValue) {
                        var predicateType = typeof predicate;
                        if (predicateType == "function")    // Given a function; run it against the data value
                            return predicate(object);
                        else if (predicateType == "string") // Given a string; treat it as a property name on the data value
                            return object[predicate];
                        else                                // Given no optionsText arg; use the data value itself
                            return defaultValue;
                    }

                    // The following functions can run at two different times:
                    // The first is when the whole array is being updated directly from this binding handler.
                    // The second is when an observable value for a specific array entry is updated.
                    // oldOptions will be empty in the first case, but will be filled with the previously generated option in the second.
                    var itemUpdate = false;
                    function optionForArrayItem(arrayEntry, index, oldOptions) {
                        if (oldOptions.length) {
                            previousSelectedValues = !valueAllowUnset && oldOptions[0].selected ? [ ko.selectExtensions.readValue(oldOptions[0]) ] : [];
                            itemUpdate = true;
                        }
                        var option = element.ownerDocument.createElement("option");
                        if (arrayEntry === captionPlaceholder) {
                            ko.utils.setTextContent(option, allBindings.get('optionsCaption'));
                            ko.selectExtensions.writeValue(option, undefined);
                        } else {
                            // Apply a value to the option element
                            var optionValue = applyToObject(arrayEntry, allBindings.get('optionsValue'), arrayEntry);
                            ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));

                            // Apply some text to the option element
                            var optionText = applyToObject(arrayEntry, allBindings.get('optionsText'), optionValue);
                            ko.utils.setTextContent(option, optionText);
                        }
                        return [option];
                    }

                    // By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection
                    // problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208
                    arrayToDomNodeChildrenOptions['beforeRemove'] =
                        function (option) {
                            element.removeChild(option);
                        };

                    function setSelectionCallback(arrayEntry, newOptions) {
                        if (itemUpdate && valueAllowUnset) {
                            // The model value is authoritative, so make sure its value is the one selected
                            ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
                        } else if (previousSelectedValues.length) {
                            // 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.
                            var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
                            ko.utils.setOptionNodeSelectionState(newOptions[0], 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"]);
                            }
                        }
                    }

                    var callback = setSelectionCallback;
                    if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == "function") {
                        callback = function(arrayEntry, newOptions) {
                            setSelectionCallback(arrayEntry, newOptions);
                            ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);
                        }
                    }

                    ko.utils.setDomNodeChildrenFromArrayMapping(element, filteredArray, optionForArrayItem, arrayToDomNodeChildrenOptions, callback);

                    if (!valueAllowUnset) {
                        // Determine if the selection has changed as a result of updating the options list
                        var selectionChanged;
                        if (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 < 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.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
                        }
                    }

                    if (valueAllowUnset || ko.computedContext.isInitial()) {
                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
                    }

                    // Workaround for IE bug
                    ko.utils.ensureSelectElementIsRenderedCorrectly(element);

                    if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20)
                        element.scrollTop = previousScrollTop;
                }
            };
            ko.bindingHandlers['options'].optionValueDomDataKey = ko.utils.domData.nextKey();
            ko.bindingHandlers['selectedOptions'] = {
                'init': function (element, valueAccessor, allBindings) {
                    function updateFromView() {
                        var value = valueAccessor(), valueToWrite = [];
                        ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
                            if (node.selected)
                                valueToWrite.push(ko.selectExtensions.readValue(node));
                        });
                        ko.expressionRewriting.writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);
                    }

                    function updateFromModel() {
                        var newValue = ko.utils.unwrapObservable(valueAccessor()),
                            previousScrollTop = element.scrollTop;

                        if (newValue && typeof newValue.length == "number") {
                            ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
                                var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
                                if (node.selected != isSelected) {      // This check prevents flashing of the select element in IE
                                    ko.utils.setOptionNodeSelectionState(node, isSelected);
                                }
                            });
                        }

                        element.scrollTop = previousScrollTop;
                    }

                    if (ko.utils.tagNameLower(element) != "select") {
                        throw new Error("selectedOptions binding applies only to SELECT elements");
                    }

                    var updateFromModelComputed;
                    ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
                        if (!updateFromModelComputed) {
                            ko.utils.registerEventHandler(element, "change", updateFromView);
                            updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
                        } else {
                            updateFromView();
                        }
                    }, null, { 'notifyImmediately': true });
                },
                'update': function() {} // Keep for backwards compatibility with code that may have wrapped binding
            };
            ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;
            ko.bindingHandlers['style'] = {
                'update': function (element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor() || {});
                    ko.utils.objectForEach(value, function(styleName, styleValue) {
                        styleValue = ko.utils.unwrapObservable(styleValue);

                        if (styleValue === null || styleValue === undefined || styleValue === false) {
                            // Empty string removes the value, whereas null/undefined have no effect
                            styleValue = "";
                        }

                        if (jQueryInstance) {
                            jQueryInstance(element)['css'](styleName, styleValue);
                        } else if (/^--/.test(styleName)) {
                            // Is styleName a custom CSS property?
                            element.style.setProperty(styleName, styleValue);
                        } else {
                            styleName = styleName.replace(/-(\w)/g, function (all, letter) {
                                return letter.toUpperCase();
                            });

                            var previousStyle = element.style[styleName];
                            element.style[styleName] = styleValue;

                            if (styleValue !== previousStyle && element.style[styleName] == previousStyle && !isNaN(styleValue)) {
                                element.style[styleName] = styleValue + "px";
                            }
                        }
                    });
                }
            };
            ko.bindingHandlers['submit'] = {
                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                    if (typeof valueAccessor() != "function")
                        throw new Error("The value for a submit binding must be a function");
                    ko.utils.registerEventHandler(element, "submit", function (event) {
                        var handlerReturnValue;
                        var value = valueAccessor();
                        try { handlerReturnValue = value.call(bindingContext['$data'], element); }
                        finally {
                            if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
                                if (event.preventDefault)
                                    event.preventDefault();
                                else
                                    event.returnValue = false;
                            }
                        }
                    });
                }
            };
            ko.bindingHandlers['text'] = {
                'init': function() {
                    // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
                    // It should also make things faster, as we no longer have to consider whether the text node might be bindable.
                    return { 'controlsDescendantBindings': true };
                },
                'update': function (element, valueAccessor) {
                    ko.utils.setTextContent(element, valueAccessor());
                }
            };
            ko.virtualElements.allowedBindings['text'] = true;
            (function () {

                if (window && window.navigator) {
                    var parseVersion = function (matches) {
                        if (matches) {
                            return parseFloat(matches[1]);
                        }
                    };

                    // Detect various browser versions because some old versions don't fully support the 'input' event
                    var userAgent = window.navigator.userAgent,
                        operaVersion, chromeVersion, safariVersion, firefoxVersion, ieVersion, edgeVersion;

                    (operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()))
                    || (edgeVersion = parseVersion(userAgent.match(/Edge\/([^ ]+)$/)))
                    || (chromeVersion = parseVersion(userAgent.match(/Chrome\/([^ ]+)/)))
                    || (safariVersion = parseVersion(userAgent.match(/Version\/([^ ]+) Safari/)))
                    || (firefoxVersion = parseVersion(userAgent.match(/Firefox\/([^ ]+)/)))
                    || (ieVersion = ko.utils.ieVersion || parseVersion(userAgent.match(/MSIE ([^ ]+)/)))      // Detects up to IE 10
                    || (ieVersion = parseVersion(userAgent.match(/rv:([^ )]+)/)));      // Detects IE 11
                }

// IE 8 and 9 have bugs that prevent the normal events from firing when the value changes.
// But it does fire the 'selectionchange' event on many of those, presumably because the
// cursor is moving and that counts as the selection changing. The 'selectionchange' event is
// fired at the document level only and doesn't directly indicate which element changed. We
// set up just one event handler for the document and use 'activeElement' to determine which
// element was changed.
                if (ieVersion >= 8 && ieVersion < 10) {
                    var selectionChangeRegisteredName = ko.utils.domData.nextKey(),
                        selectionChangeHandlerName = ko.utils.domData.nextKey();
                    var selectionChangeHandler = function(event) {
                        var target = this.activeElement,
                            handler = target && ko.utils.domData.get(target, selectionChangeHandlerName);
                        if (handler) {
                            handler(event);
                        }
                    };
                    var registerForSelectionChangeEvent = function (element, handler) {
                        var ownerDoc = element.ownerDocument;
                        if (!ko.utils.domData.get(ownerDoc, selectionChangeRegisteredName)) {
                            ko.utils.domData.set(ownerDoc, selectionChangeRegisteredName, true);
                            ko.utils.registerEventHandler(ownerDoc, 'selectionchange', selectionChangeHandler);
                        }
                        ko.utils.domData.set(element, selectionChangeHandlerName, handler);
                    };
                }

                ko.bindingHandlers['textInput'] = {
                    'init': function (element, valueAccessor, allBindings) {

                        var previousElementValue = element.value,
                            timeoutHandle,
                            elementValueBeforeEvent;

                        var updateModel = function (event) {
                            clearTimeout(timeoutHandle);
                            elementValueBeforeEvent = timeoutHandle = undefined;

                            var elementValue = element.value;
                            if (previousElementValue !== elementValue) {
                                // Provide a way for tests to know exactly which event was processed
                                if (DEBUG && event) element['_ko_textInputProcessedEvent'] = event.type;
                                previousElementValue = elementValue;
                                ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
                            }
                        };

                        var deferUpdateModel = function (event) {
                            if (!timeoutHandle) {
                                // The elementValueBeforeEvent variable is set *only* during the brief gap between an
                                // event firing and the updateModel function running. This allows us to ignore model
                                // updates that are from the previous state of the element, usually due to techniques
                                // such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
                                elementValueBeforeEvent = element.value;
                                var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;
                                timeoutHandle = ko.utils.setTimeout(handler, 4);
                            }
                        };

                        // IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);
                        // so we'll make sure all updates are asynchronous
                        var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel,
                            ourUpdate = false;

                        var updateView = function () {
                            var modelValue = ko.utils.unwrapObservable(valueAccessor());

                            if (modelValue === null || modelValue === undefined) {
                                modelValue = '';
                            }

                            if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
                                ko.utils.setTimeout(updateView, 4);
                                return;
                            }

                            // Update the element only if the element and model are different. On some browsers, updating the value
                            // will move the cursor to the end of the input, which would be bad while the user is typing.
                            if (element.value !== modelValue) {
                                ourUpdate = true;  // Make sure we ignore events (propertychange) that result from updating the value
                                element.value = modelValue;
                                ourUpdate = false;
                                previousElementValue = element.value; // In case the browser changes the value (see #2281)
                            }
                        };

                        var onEvent = function (event, handler) {
                            ko.utils.registerEventHandler(element, event, handler);
                        };

                        if (DEBUG && ko.bindingHandlers['textInput']['_forceUpdateOn']) {
                            // Provide a way for tests to specify exactly which events are bound
                            ko.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function(eventName) {
                                if (eventName.slice(0,5) == 'after') {
                                    onEvent(eventName.slice(5), deferUpdateModel);
                                } else {
                                    onEvent(eventName, updateModel);
                                }
                            });
                        } else {
                            if (ieVersion) {
                                // All versions (including 11) of Internet Explorer have a bug that they don't generate an input or propertychange event when ESC is pressed
                                onEvent('keypress', updateModel);
                            }
                            if (ieVersion < 11) {
                                // Internet Explorer <= 8 doesn't support the 'input' event, but does include 'propertychange' that fires whenever
                                // any property of an element changes. Unlike 'input', it also fires if a property is changed from JavaScript code,
                                // but that's an acceptable compromise for this binding. IE 9 and 10 support 'input', but since they don't always
                                // fire it when using autocomplete, we'll use 'propertychange' for them also.
                                onEvent('propertychange', function(event) {
                                    if (!ourUpdate && event.propertyName === 'value') {
                                        ieUpdateModel(event);
                                    }
                                });
                            }
                            if (ieVersion == 8) {
                                // IE 8 has a bug where it fails to fire 'propertychange' on the first update following a value change from
                                // JavaScript code. It also doesn't fire if you clear the entire value. To fix this, we bind to the following
                                // events too.
                                onEvent('keyup', updateModel);      // A single keystoke
                                onEvent('keydown', updateModel);    // The first character when a key is held down
                            }
                            if (registerForSelectionChangeEvent) {
                                // Internet Explorer 9 doesn't fire the 'input' event when deleting text, including using
                                // the backspace, delete, or ctrl-x keys, clicking the 'x' to clear the input, dragging text
                                // out of the field, and cutting or deleting text using the context menu. 'selectionchange'
                                // can detect all of those except dragging text out of the field, for which we use 'dragend'.
                                // These are also needed in IE8 because of the bug described above.
                                registerForSelectionChangeEvent(element, ieUpdateModel);  // 'selectionchange' covers cut, paste, drop, delete, etc.
                                onEvent('dragend', deferUpdateModel);
                            }

                            if (!ieVersion || ieVersion >= 9) {
                                // All other supported browsers support the 'input' event, which fires whenever the content of the element is changed
                                // through the user interface.
                                onEvent('input', ieUpdateModel);
                            }

                            if (safariVersion < 5 && ko.utils.tagNameLower(element) === "textarea") {
                                // Safari <5 doesn't fire the 'input' event for <textarea> elements (it does fire 'textInput'
                                // but only when typing). So we'll just catch as much as we can with keydown, cut, and paste.
                                onEvent('keydown', deferUpdateModel);
                                onEvent('paste', deferUpdateModel);
                                onEvent('cut', deferUpdateModel);
                            } else if (operaVersion < 11) {
                                // Opera 10 doesn't always fire the 'input' event for cut, paste, undo & drop operations.
                                // We can try to catch some of those using 'keydown'.
                                onEvent('keydown', deferUpdateModel);
                            } else if (firefoxVersion < 4.0) {
                                // Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete
                                onEvent('DOMAutoComplete', updateModel);

                                // Firefox <=3.5 doesn't fire the 'input' event when text is dropped into the input.
                                onEvent('dragdrop', updateModel);       // <3.5
                                onEvent('drop', updateModel);           // 3.5
                            } else if (edgeVersion && element.type === "number") {
                                // Microsoft Edge doesn't fire 'input' or 'change' events for number inputs when
                                // the value is changed via the up / down arrow keys
                                onEvent('keydown', deferUpdateModel);
                            }
                        }

                        // Bind to the change event so that we can catch programmatic updates of the value that fire this event.
                        onEvent('change', updateModel);

                        // To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.)
                        onEvent('blur', updateModel);

                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
                    }
                };
                ko.expressionRewriting.twoWayBindings['textInput'] = true;

// textinput is an alias for textInput
                ko.bindingHandlers['textinput'] = {
                    // preprocess is the only way to set up a full alias
                    'preprocess': function (value, name, addBinding) {
                        addBinding('textInput', value);
                    }
                };

            })();ko.bindingHandlers['uniqueName'] = {
                'init': function (element, valueAccessor) {
                    if (valueAccessor()) {
                        var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
                        ko.utils.setElementName(element, name);
                    }
                }
            };
            ko.bindingHandlers['uniqueName'].currentIndex = 0;
            ko.bindingHandlers['using'] = {
                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                    var options;

                    if (allBindings['has']('as')) {
                        options = { 'as': allBindings.get('as'), 'noChildContext': allBindings.get('noChildContext') };
                    }

                    var innerContext = bindingContext['createChildContext'](valueAccessor, options);
                    ko.applyBindingsToDescendants(innerContext, element);

                    return { 'controlsDescendantBindings': true };
                }
            };
            ko.virtualElements.allowedBindings['using'] = true;
            ko.bindingHandlers['value'] = {
                'init': function (element, valueAccessor, allBindings) {
                    var tagName = ko.utils.tagNameLower(element),
                        isInputElement = tagName == "input";

                    // If the value binding is placed on a radio/checkbox, then just pass through to checkedValue and quit
                    if (isInputElement && (element.type == "checkbox" || element.type == "radio")) {
                        ko.applyBindingAccessorsToNode(element, { 'checkedValue': valueAccessor });
                        return;
                    }

                    var eventsToCatch = [];
                    var requestedEventsToCatch = allBindings.get("valueUpdate");
                    var propertyChangedFired = false;
                    var elementValueBeforeEvent = null;

                    if (requestedEventsToCatch) {
                        // Allow both individual event names, and arrays of event names
                        if (typeof requestedEventsToCatch == "string") {
                            eventsToCatch = [requestedEventsToCatch];
                        } else {
                            eventsToCatch = ko.utils.arrayGetDistinctValues(requestedEventsToCatch);
                        }
                        ko.utils.arrayRemoveItem(eventsToCatch, "change");  // We'll subscribe to "change" events later
                    }

                    var valueUpdateHandler = function() {
                        elementValueBeforeEvent = null;
                        propertyChangedFired = false;
                        var modelValue = valueAccessor();
                        var elementValue = ko.selectExtensions.readValue(element);
                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);
                    }

                    // Workaround for https://github.com/SteveSanderson/knockout/issues/122
                    // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
                    var ieAutoCompleteHackNeeded = ko.utils.ieVersion && isInputElement && element.type == "text"
                        && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
                    if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
                        ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
                        ko.utils.registerEventHandler(element, "focus", function () { propertyChangedFired = false });
                        ko.utils.registerEventHandler(element, "blur", function() {
                            if (propertyChangedFired) {
                                valueUpdateHandler();
                            }
                        });
                    }

                    ko.utils.arrayForEach(eventsToCatch, function(eventName) {
                        // The syntax "after<eventname>" means "run the handler asynchronously after the event"
                        // This is useful, for example, to catch "keydown" events after the browser has updated the control
                        // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
                        var handler = valueUpdateHandler;
                        if (ko.utils.stringStartsWith(eventName, "after")) {
                            handler = function() {
                                // The elementValueBeforeEvent variable is non-null *only* during the brief gap between
                                // a keyX event firing and the valueUpdateHandler running, which is scheduled to happen
                                // at the earliest asynchronous opportunity. We store this temporary information so that
                                // if, between keyX and valueUpdateHandler, the underlying model value changes separately,
                                // we can overwrite that model value change with the value the user just typed. Otherwise,
                                // techniques like rateLimit can trigger model changes at critical moments that will
                                // override the user's inputs, causing keystrokes to be lost.
                                elementValueBeforeEvent = ko.selectExtensions.readValue(element);
                                ko.utils.setTimeout(valueUpdateHandler, 0);
                            };
                            eventName = eventName.substring("after".length);
                        }
                        ko.utils.registerEventHandler(element, eventName, handler);
                    });

                    var updateFromModel;

                    if (isInputElement && element.type == "file") {
                        // For file input elements, can only write the empty string
                        updateFromModel = function () {
                            var newValue = ko.utils.unwrapObservable(valueAccessor());
                            if (newValue === null || newValue === undefined || newValue === "") {
                                element.value = "";
                            } else {
                                ko.dependencyDetection.ignore(valueUpdateHandler);  // reset the model to match the element
                            }
                        }
                    } else {
                        updateFromModel = function () {
                            var newValue = ko.utils.unwrapObservable(valueAccessor());
                            var elementValue = ko.selectExtensions.readValue(element);

                            if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
                                ko.utils.setTimeout(updateFromModel, 0);
                                return;
                            }

                            var valueHasChanged = newValue !== elementValue;

                            if (valueHasChanged || elementValue === undefined) {
                                if (tagName === "select") {
                                    var allowUnset = allBindings.get('valueAllowUnset');
                                    ko.selectExtensions.writeValue(element, newValue, allowUnset);
                                    if (!allowUnset && newValue !== ko.selectExtensions.readValue(element)) {
                                        // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
                                        // because you're not allowed to have a model value that disagrees with a visible UI selection.
                                        ko.dependencyDetection.ignore(valueUpdateHandler);
                                    }
                                } else {
                                    ko.selectExtensions.writeValue(element, newValue);
                                }
                            }
                        };
                    }

                    if (tagName === "select") {
                        var updateFromModelComputed;
                        ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
                            if (!updateFromModelComputed) {
                                ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
                                updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
                            } else if (allBindings.get('valueAllowUnset')) {
                                updateFromModel();
                            } else {
                                valueUpdateHandler();
                            }
                        }, null, { 'notifyImmediately': true });
                    } else {
                        ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
                        ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
                    }
                },
                'update': function() {} // Keep for backwards compatibility with code that may have wrapped value binding
            };
            ko.expressionRewriting.twoWayBindings['value'] = true;
            ko.bindingHandlers['visible'] = {
                'update': function (element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor());
                    var isCurrentlyVisible = !(element.style.display == "none");
                    if (value && !isCurrentlyVisible)
                        element.style.display = "";
                    else if ((!value) && isCurrentlyVisible)
                        element.style.display = "none";
                }
            };

            ko.bindingHandlers['hidden'] = {
                'update': function (element, valueAccessor) {
                    ko.bindingHandlers['visible']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
                }
            };
// 'click' is just a shorthand for the usual full-length event:{click:handler}
            makeEventHandlerShortcut('click');
// If you want to make a custom template engine,
//
// [1] Inherit from this class (like ko.nativeTemplateEngine does)
// [2] Override 'renderTemplateSource', supplying a function with this signature:
//
//        function (templateSource, bindingContext, options) {
//            // - templateSource.text() is the text of the template you should render
//            // - bindingContext.$data is the data you should pass into the template
//            //   - you might also want to make bindingContext.$parent, bindingContext.$parents,
//            //     and bindingContext.$root available in the template too
//            // - options gives you access to any other properties set on "data-bind: { template: options }"
//            // - templateDocument is the document object of the template
//            //
//            // Return value: an array of DOM nodes
//        }
//
// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
//
//        function (script) {
//            // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
//            //               For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
//        }
//
//     This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
//     If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
//     and then you don't need to override 'createJavaScriptEvaluatorBlock'.

            ko.templateEngine = function () { };

            ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
                throw new Error("Override renderTemplateSource");
            };

            ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
                throw new Error("Override createJavaScriptEvaluatorBlock");
            };

            ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
                // Named template
                if (typeof template == "string") {
                    templateDocument = templateDocument || document;
                    var elem = templateDocument.getElementById(template);
                    if (!elem)
                        throw new Error("Cannot find template with ID " + template);
                    return new ko.templateSources.domElement(elem);
                } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
                    // Anonymous template
                    return new ko.templateSources.anonymousTemplate(template);
                } else
                    throw new Error("Unknown template type: " + template);
            };

            ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
                var templateSource = this['makeTemplateSource'](template, templateDocument);
                return this['renderTemplateSource'](templateSource, bindingContext, options, templateDocument);
            };

            ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
                // Skip rewriting if requested
                if (this['allowTemplateRewriting'] === false)
                    return true;
                return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
            };

            ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
                var templateSource = this['makeTemplateSource'](template, templateDocument);
                var rewritten = rewriterCallback(templateSource['text']());
                templateSource['text'](rewritten);
                templateSource['data']("isRewritten", true);
            };

            ko.exportSymbol('templateEngine', ko.templateEngine);

            ko.templateRewriting = (function () {
                var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi;
                var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;

                function validateDataBindValuesForRewriting(keyValueArray) {
                    var allValidators = ko.expressionRewriting.bindingRewriteValidators;
                    for (var i = 0; i < keyValueArray.length; i++) {
                        var key = keyValueArray[i]['key'];
                        if (Object.prototype.hasOwnProperty.call(allValidators, key)) {
                            var validator = allValidators[key];

                            if (typeof validator === "function") {
                                var possibleErrorMessage = validator(keyValueArray[i]['value']);
                                if (possibleErrorMessage)
                                    throw new Error(possibleErrorMessage);
                            } else if (!validator) {
                                throw new Error("This template engine does not support the '" + key + "' binding within its templates");
                            }
                        }
                    }
                }

                function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, nodeName, templateEngine) {
                    var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
                    validateDataBindValuesForRewriting(dataBindKeyValueArray);
                    var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray, {'valueAccessors':true});

                    // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
                    // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
                    // extra indirection.
                    var applyBindingsToNextSiblingScript =
                        "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()},'" + nodeName.toLowerCase() + "')";
                    return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
                }

                return {
                    ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
                        if (!templateEngine['isTemplateRewritten'](template, templateDocument))
                            templateEngine['rewriteTemplate'](template, function (htmlString) {
                                return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
                            }, templateDocument);
                    },

                    memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
                        return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[4], /* tagToRetain: */ arguments[1], /* nodeName: */ arguments[2], templateEngine);
                        }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", /* nodeName: */ "#comment", templateEngine);
                        });
                    },

                    applyMemoizedBindingsToNextSibling: function (bindings, nodeName) {
                        return ko.memoization.memoize(function (domNode, bindingContext) {
                            var nodeToBind = domNode.nextSibling;
                            if (nodeToBind && nodeToBind.nodeName.toLowerCase() === nodeName) {
                                ko.applyBindingAccessorsToNode(nodeToBind, bindings, bindingContext);
                            }
                        });
                    }
                }
            })();


// Exported only because it has to be referenced by string lookup from within rewritten template
            ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
            (function() {
                // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
                // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
                //
                // Two are provided by default:
                //  1. ko.templateSources.domElement       - reads/writes the text content of an arbitrary DOM element
                //  2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
                //                                           without reading/writing the actual element text content, since it will be overwritten
                //                                           with the rendered template output.
                // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
                // Template sources need to have the following functions:
                //   text() 			- returns the template text from your storage location
                //   text(value)		- writes the supplied template text to your storage location
                //   data(key)			- reads values stored using data(key, value) - see below
                //   data(key, value)	- associates "value" with this template and the key "key". Is used to store information like "isRewritten".
                //
                // Optionally, template sources can also have the following functions:
                //   nodes()            - returns a DOM element containing the nodes of this template, where available
                //   nodes(value)       - writes the given DOM element to your storage location
                // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
                // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
                //
                // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
                // using and overriding "makeTemplateSource" to return an instance of your custom template source.

                ko.templateSources = {};

                // ---- ko.templateSources.domElement -----

                // template types
                var templateScript = 1,
                    templateTextArea = 2,
                    templateTemplate = 3,
                    templateElement = 4;

                ko.templateSources.domElement = function(element) {
                    this.domElement = element;

                    if (element) {
                        var tagNameLower = ko.utils.tagNameLower(element);
                        this.templateType =
                            tagNameLower === "script" ? templateScript :
                                tagNameLower === "textarea" ? templateTextArea :
                                    // For browsers with proper <template> element support, where the .content property gives a document fragment
                                    tagNameLower == "template" && element.content && element.content.nodeType === 11 ? templateTemplate :
                                        templateElement;
                    }
                }

                ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
                    var elemContentsProperty = this.templateType === templateScript ? "text"
                        : this.templateType === templateTextArea ? "value"
                            : "innerHTML";

                    if (arguments.length == 0) {
                        return this.domElement[elemContentsProperty];
                    } else {
                        var valueToWrite = arguments[0];
                        if (elemContentsProperty === "innerHTML")
                            ko.utils.setHtml(this.domElement, valueToWrite);
                        else
                            this.domElement[elemContentsProperty] = valueToWrite;
                    }
                };

                var dataDomDataPrefix = ko.utils.domData.nextKey() + "_";
                ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
                    if (arguments.length === 1) {
                        return ko.utils.domData.get(this.domElement, dataDomDataPrefix + key);
                    } else {
                        ko.utils.domData.set(this.domElement, dataDomDataPrefix + key, arguments[1]);
                    }
                };

                var templatesDomDataKey = ko.utils.domData.nextKey();
                function getTemplateDomData(element) {
                    return ko.utils.domData.get(element, templatesDomDataKey) || {};
                }
                function setTemplateDomData(element, data) {
                    ko.utils.domData.set(element, templatesDomDataKey, data);
                }

                ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
                    var element = this.domElement;
                    if (arguments.length == 0) {
                        var templateData = getTemplateDomData(element),
                            nodes = templateData.containerData || (
                                this.templateType === templateTemplate ? element.content :
                                    this.templateType === templateElement ? element :
                                        undefined);
                        if (!nodes || templateData.alwaysCheckText) {
                            // If the template is associated with an element that stores the template as text,
                            // parse and cache the nodes whenever there's new text content available. This allows
                            // the user to update the template content by updating the text of template node.
                            var text = this['text']();
                            if (text && text !== templateData.textData) {
                                nodes = ko.utils.parseHtmlForTemplateNodes(text, element.ownerDocument);
                                setTemplateDomData(element, {containerData: nodes, textData: text, alwaysCheckText: true});
                            }
                        }
                        return nodes;
                    } else {
                        var valueToWrite = arguments[0];
                        if (this.templateType !== undefined) {
                            this['text']("");   // clear the text from the node
                        }
                        setTemplateDomData(element, {containerData: valueToWrite});
                    }
                };

                // ---- ko.templateSources.anonymousTemplate -----
                // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
                // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
                // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.

                ko.templateSources.anonymousTemplate = function(element) {
                    this.domElement = element;
                }
                ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
                ko.templateSources.anonymousTemplate.prototype.constructor = ko.templateSources.anonymousTemplate;
                ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
                    if (arguments.length == 0) {
                        var templateData = getTemplateDomData(this.domElement);
                        if (templateData.textData === undefined && templateData.containerData)
                            templateData.textData = templateData.containerData.innerHTML;
                        return templateData.textData;
                    } else {
                        var valueToWrite = arguments[0];
                        setTemplateDomData(this.domElement, {textData: valueToWrite});
                    }
                };

                ko.exportSymbol('templateSources', ko.templateSources);
                ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
                ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
            })();
            (function () {
                var _templateEngine;
                ko.setTemplateEngine = function (templateEngine) {
                    if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
                        throw new Error("templateEngine must inherit from ko.templateEngine");
                    _templateEngine = templateEngine;
                }

                function invokeForEachNodeInContinuousRange(firstNode, lastNode, action) {
                    var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
                    while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
                        nextInQueue = ko.virtualElements.nextSibling(node);
                        action(node, nextInQueue);
                    }
                }

                function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
                    // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
                    // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
                    // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
                    // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
                    // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)

                    if (continuousNodeArray.length) {
                        var firstNode = continuousNodeArray[0],
                            lastNode = continuousNodeArray[continuousNodeArray.length - 1],
                            parentNode = firstNode.parentNode,
                            provider = ko.bindingProvider['instance'],
                            preprocessNode = provider['preprocessNode'];

                        if (preprocessNode) {
                            invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node, nextNodeInRange) {
                                var nodePreviousSibling = node.previousSibling;
                                var newNodes = preprocessNode.call(provider, node);
                                if (newNodes) {
                                    if (node === firstNode)
                                        firstNode = newNodes[0] || nextNodeInRange;
                                    if (node === lastNode)
                                        lastNode = newNodes[newNodes.length - 1] || nodePreviousSibling;
                                }
                            });

                            // Because preprocessNode can change the nodes, including the first and last nodes, update continuousNodeArray to match.
                            // We need the full set, including inner nodes, because the unmemoize step might remove the first node (and so the real
                            // first node needs to be in the array).
                            continuousNodeArray.length = 0;
                            if (!firstNode) { // preprocessNode might have removed all the nodes, in which case there's nothing left to do
                                return;
                            }
                            if (firstNode === lastNode) {
                                continuousNodeArray.push(firstNode);
                            } else {
                                continuousNodeArray.push(firstNode, lastNode);
                                ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
                            }
                        }

                        // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
                        // whereas a regular applyBindings won't introduce new memoized nodes
                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {
                            if (node.nodeType === 1 || node.nodeType === 8)
                                ko.applyBindings(bindingContext, node);
                        });
                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {
                            if (node.nodeType === 1 || node.nodeType === 8)
                                ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
                        });

                        // Make sure any changes done by applyBindings or unmemoize are reflected in the array
                        ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
                    }
                }

                function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
                    return nodeOrNodeArray.nodeType ? nodeOrNodeArray
                        : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
                            : null;
                }

                function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
                    options = options || {};
                    var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
                    var templateDocument = (firstTargetNode || template || {}).ownerDocument;
                    var templateEngineToUse = (options['templateEngine'] || _templateEngine);
                    ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
                    var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);

                    // Loosely check result is an array of DOM nodes
                    if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
                        throw new Error("Template engine must return an array of DOM nodes");

                    var haveAddedNodesToParent = false;
                    switch (renderMode) {
                        case "replaceChildren":
                            ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
                            haveAddedNodesToParent = true;
                            break;
                        case "replaceNode":
                            ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
                            haveAddedNodesToParent = true;
                            break;
                        case "ignoreTargetNode": break;
                        default:
                            throw new Error("Unknown renderMode: " + renderMode);
                    }

                    if (haveAddedNodesToParent) {
                        activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
                        if (options['afterRender']) {
                            ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext[options['as'] || '$data']]);
                        }
                        if (renderMode == "replaceChildren") {
                            ko.bindingEvent.notify(targetNodeOrNodeArray, ko.bindingEvent.childrenComplete);
                        }
                    }

                    return renderedNodesArray;
                }

                function resolveTemplateName(template, data, context) {
                    // The template can be specified as:
                    if (ko.isObservable(template)) {
                        // 1. An observable, with string value
                        return template();
                    } else if (typeof template === 'function') {
                        // 2. A function of (data, context) returning a string
                        return template(data, context);
                    } else {
                        // 3. A string
                        return template;
                    }
                }

                ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
                    options = options || {};
                    if ((options['templateEngine'] || _templateEngine) == undefined)
                        throw new Error("Set a template engine before calling renderTemplate");
                    renderMode = renderMode || "replaceChildren";

                    if (targetNodeOrNodeArray) {
                        var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);

                        var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
                        var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;

                        return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
                            function () {
                                // Ensure we've got a proper binding context to work with
                                var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
                                    ? dataOrBindingContext
                                    : new ko.bindingContext(dataOrBindingContext, null, null, null, { "exportDependencies": true });

                                var templateName = resolveTemplateName(template, bindingContext['$data'], bindingContext),
                                    renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);

                                if (renderMode == "replaceNode") {
                                    targetNodeOrNodeArray = renderedNodesArray;
                                    firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
                                }
                            },
                            null,
                            { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
                        );
                    } else {
                        // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
                        return ko.memoization.memoize(function (domNode) {
                            ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
                        });
                    }
                };

                ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
                    // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
                    // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
                    var arrayItemContext, asName = options['as'];

                    // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
                    var executeTemplateForArrayItem = function (arrayValue, index) {
                        // Support selecting template as a function of the data being rendered
                        arrayItemContext = parentBindingContext['createChildContext'](arrayValue, {
                            'as': asName,
                            'noChildContext': options['noChildContext'],
                            'extend': function(context) {
                                context['$index'] = index;
                                if (asName) {
                                    context[asName + "Index"] = index;
                                }
                            }
                        });

                        var templateName = resolveTemplateName(template, arrayValue, arrayItemContext);
                        return executeTemplate(targetNode, "ignoreTargetNode", templateName, arrayItemContext, options);
                    };

                    // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
                    var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
                        activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
                        if (options['afterRender'])
                            options['afterRender'](addedNodesArray, arrayValue);

                        // release the "cache" variable, so that it can be collected by
                        // the GC when its value isn't used from within the bindings anymore.
                        arrayItemContext = null;
                    };

                    var setDomNodeChildrenFromArrayMapping = function (newArray, changeList) {
                        // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
                        // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
                        ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, newArray, executeTemplateForArrayItem, options, activateBindingsCallback, changeList]);
                        ko.bindingEvent.notify(targetNode, ko.bindingEvent.childrenComplete);
                    };

                    var shouldHideDestroyed = (options['includeDestroyed'] === false) || (ko.options['foreachHidesDestroyed'] && !options['includeDestroyed']);

                    if (!shouldHideDestroyed && !options['beforeRemove'] && ko.isObservableArray(arrayOrObservableArray)) {
                        setDomNodeChildrenFromArrayMapping(arrayOrObservableArray.peek());

                        var subscription = arrayOrObservableArray.subscribe(function (changeList) {
                            setDomNodeChildrenFromArrayMapping(arrayOrObservableArray(), changeList);
                        }, null, "arrayChange");
                        subscription.disposeWhenNodeIsRemoved(targetNode);

                        return subscription;
                    } else {
                        return ko.dependentObservable(function () {
                            var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
                            if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
                                unwrappedArray = [unwrappedArray];

                            if (shouldHideDestroyed) {
                                // Filter out any entries marked as destroyed
                                unwrappedArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
                                    return item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
                                });
                            }
                            setDomNodeChildrenFromArrayMapping(unwrappedArray);

                        }, null, { disposeWhenNodeIsRemoved: targetNode });
                    }
                };

                var templateComputedDomDataKey = ko.utils.domData.nextKey();
                function disposeOldComputedAndStoreNewOne(element, newComputed) {
                    var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
                    if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
                        oldComputed.dispose();
                    ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && (!newComputed.isActive || newComputed.isActive())) ? newComputed : undefined);
                }

                var cleanContainerDomDataKey = ko.utils.domData.nextKey();
                ko.bindingHandlers['template'] = {
                    'init': function(element, valueAccessor) {
                        // Support anonymous templates
                        var bindingValue = ko.utils.unwrapObservable(valueAccessor());
                        if (typeof bindingValue == "string" || 'name' in bindingValue) {
                            // It's a named template - clear the element
                            ko.virtualElements.emptyNode(element);
                        } else if ('nodes' in bindingValue) {
                            // We've been given an array of DOM nodes. Save them as the template source.
                            // There is no known use case for the node array being an observable array (if the output
                            // varies, put that behavior *into* your template - that's what templates are for), and
                            // the implementation would be a mess, so assert that it's not observable.
                            var nodes = bindingValue['nodes'] || [];
                            if (ko.isObservable(nodes)) {
                                throw new Error('The "nodes" option must be a plain, non-observable array.');
                            }

                            // If the nodes are already attached to a KO-generated container, we reuse that container without moving the
                            // elements to a new one (we check only the first node, as the nodes are always moved together)
                            var container = nodes[0] && nodes[0].parentNode;
                            if (!container || !ko.utils.domData.get(container, cleanContainerDomDataKey)) {
                                container = ko.utils.moveCleanedNodesToContainerElement(nodes);
                                ko.utils.domData.set(container, cleanContainerDomDataKey, true);
                            }

                            new ko.templateSources.anonymousTemplate(element)['nodes'](container);
                        } else {
                            // It's an anonymous template - store the element contents, then clear the element
                            var templateNodes = ko.virtualElements.childNodes(element);
                            if (templateNodes.length > 0) {
                                var container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
                                new ko.templateSources.anonymousTemplate(element)['nodes'](container);
                            } else {
                                throw new Error("Anonymous template defined, but no template content was provided");
                            }
                        }
                        return { 'controlsDescendantBindings': true };
                    },
                    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                        var value = valueAccessor(),
                            options = ko.utils.unwrapObservable(value),
                            shouldDisplay = true,
                            templateComputed = null,
                            template;

                        if (typeof options == "string") {
                            template = value;
                            options = {};
                        } else {
                            template = 'name' in options ? options['name'] : element;

                            // Support "if"/"ifnot" conditions
                            if ('if' in options)
                                shouldDisplay = ko.utils.unwrapObservable(options['if']);
                            if (shouldDisplay && 'ifnot' in options)
                                shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);

                            // Don't show anything if an empty name is given (see #2446)
                            if (shouldDisplay && !template) {
                                shouldDisplay = false;
                            }
                        }

                        if ('foreach' in options) {
                            // Render once for each data point (treating data set as empty if shouldDisplay==false)
                            var dataArray = (shouldDisplay && options['foreach']) || [];
                            templateComputed = ko.renderTemplateForEach(template, dataArray, options, element, bindingContext);
                        } else if (!shouldDisplay) {
                            ko.virtualElements.emptyNode(element);
                        } else {
                            // Render once for this single data point (or use the viewModel if no data was provided)
                            var innerBindingContext = bindingContext;
                            if ('data' in options) {
                                innerBindingContext = bindingContext['createChildContext'](options['data'], {
                                    'as': options['as'],
                                    'noChildContext': options['noChildContext'],
                                    'exportDependencies': true
                                });
                            }
                            templateComputed = ko.renderTemplate(template, innerBindingContext, options, element);
                        }

                        // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
                        disposeOldComputedAndStoreNewOne(element, templateComputed);
                    }
                };

                // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
                ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
                    var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);

                    if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
                        return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)

                    if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
                        return null; // Named templates can be rewritten, so return "no error"
                    return "This template engine does not support anonymous templates nested within its templates";
                };

                ko.virtualElements.allowedBindings['template'] = true;
            })();

            ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
            ko.exportSymbol('renderTemplate', ko.renderTemplate);
// Go through the items that have been added and deleted and try to find matches between them.
            ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares) {
                if (left.length && right.length) {
                    var failedCompares, l, r, leftItem, rightItem;
                    for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
                        for (r = 0; rightItem = right[r]; ++r) {
                            if (leftItem['value'] === rightItem['value']) {
                                leftItem['moved'] = rightItem['index'];
                                rightItem['moved'] = leftItem['index'];
                                right.splice(r, 1);         // This item is marked as moved; so remove it from right list
                                failedCompares = r = 0;     // Reset failed compares count because we're checking for consecutive failures
                                break;
                            }
                        }
                        failedCompares += r;
                    }
                }
            };

            ko.utils.compareArrays = (function () {
                var statusNotInOld = 'added', statusNotInNew = 'deleted';

                // Simple calculation based on Levenshtein distance.
                function compareArrays(oldArray, newArray, options) {
                    // For backward compatibility, if the third arg is actually a bool, interpret
                    // it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.
                    options = (typeof options === 'boolean') ? { 'dontLimitMoves': options } : (options || {});
                    oldArray = oldArray || [];
                    newArray = newArray || [];

                    if (oldArray.length < newArray.length)
                        return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options);
                    else
                        return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options);
                }

                function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, options) {
                    var myMin = Math.min,
                        myMax = Math.max,
                        editDistanceMatrix = [],
                        smlIndex, smlIndexMax = smlArray.length,
                        bigIndex, bigIndexMax = bigArray.length,
                        compareRange = (bigIndexMax - smlIndexMax) || 1,
                        maxDistance = smlIndexMax + bigIndexMax + 1,
                        thisRow, lastRow,
                        bigIndexMaxForRow, bigIndexMinForRow;

                    for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
                        lastRow = thisRow;
                        editDistanceMatrix.push(thisRow = []);
                        bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
                        bigIndexMinForRow = myMax(0, smlIndex - 1);
                        for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
                            if (!bigIndex)
                                thisRow[bigIndex] = smlIndex + 1;
                            else if (!smlIndex)  // Top row - transform empty array into new array via additions
                                thisRow[bigIndex] = bigIndex + 1;
                            else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
                                thisRow[bigIndex] = lastRow[bigIndex - 1];                  // copy value (no edit)
                            else {
                                var northDistance = lastRow[bigIndex] || maxDistance;       // not in big (deletion)
                                var westDistance = thisRow[bigIndex - 1] || maxDistance;    // not in small (addition)
                                thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
                            }
                        }
                    }

                    var editScript = [], meMinusOne, notInSml = [], notInBig = [];
                    for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
                        meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
                        if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
                            notInSml.push(editScript[editScript.length] = {     // added
                                'status': statusNotInSml,
                                'value': bigArray[--bigIndex],
                                'index': bigIndex });
                        } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
                            notInBig.push(editScript[editScript.length] = {     // deleted
                                'status': statusNotInBig,
                                'value': smlArray[--smlIndex],
                                'index': smlIndex });
                        } else {
                            --bigIndex;
                            --smlIndex;
                            if (!options['sparse']) {
                                editScript.push({
                                    'status': "retained",
                                    'value': bigArray[bigIndex] });
                            }
                        }
                    }

                    // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
                    // smlIndexMax keeps the time complexity of this algorithm linear.
                    ko.utils.findMovesInArrayComparison(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);

                    return editScript.reverse();
                }

                return compareArrays;
            })();

            ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
            (function () {
                // Objective:
                // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
                //   map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
                // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
                //   so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
                //   previously mapped - retain those nodes, and just insert/delete other ones

                // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
                // You can use this, for example, to activate bindings on those nodes.

                function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
                    // Map this array value inside a dependentObservable so we re-map when any dependency changes
                    var mappedNodes = [];
                    var dependentObservable = ko.dependentObservable(function() {
                        var newMappedNodes = mapping(valueToMap, index, ko.utils.fixUpContinuousNodeArray(mappedNodes, containerNode)) || [];

                        // On subsequent evaluations, just replace the previously-inserted DOM nodes
                        if (mappedNodes.length > 0) {
                            ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);
                            if (callbackAfterAddingNodes)
                                ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
                        }

                        // Replace the contents of the mappedNodes array, thereby updating the record
                        // of which nodes would be deleted if valueToMap was itself later removed
                        mappedNodes.length = 0;
                        ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
                    }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return !ko.utils.anyDomNodeIsAttachedToDocument(mappedNodes); } });
                    return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
                }

                var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),
                    deletedItemDummyValue = ko.utils.domData.nextKey();

                ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) {
                    array = array || [];
                    if (typeof array.length == "undefined") // Coerce single value into array
                        array = [array];

                    options = options || {};
                    var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey);
                    var isFirstExecution = !lastMappingResult;

                    // Build the new mapping result
                    var newMappingResult = [];
                    var lastMappingResultIndex = 0;
                    var currentArrayIndex = 0;

                    var nodesToDelete = [];
                    var itemsToMoveFirstIndexes = [];
                    var itemsForBeforeRemoveCallbacks = [];
                    var itemsForMoveCallbacks = [];
                    var itemsForAfterAddCallbacks = [];
                    var mapData;
                    var countWaitingForRemove = 0;

                    function itemAdded(value) {
                        mapData = { arrayEntry: value, indexObservable: ko.observable(currentArrayIndex++) };
                        newMappingResult.push(mapData);
                        if (!isFirstExecution) {
                            itemsForAfterAddCallbacks.push(mapData);
                        }
                    }

                    function itemMovedOrRetained(oldPosition) {
                        mapData = lastMappingResult[oldPosition];
                        if (currentArrayIndex !== mapData.indexObservable.peek())
                            itemsForMoveCallbacks.push(mapData);
                        // Since updating the index might change the nodes, do so before calling fixUpContinuousNodeArray
                        mapData.indexObservable(currentArrayIndex++);
                        ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode);
                        newMappingResult.push(mapData);
                    }

                    function callCallback(callback, items) {
                        if (callback) {
                            for (var i = 0, n = items.length; i < n; i++) {
                                ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
                                    callback(node, i, items[i].arrayEntry);
                                });
                            }
                        }
                    }

                    if (isFirstExecution) {
                        ko.utils.arrayForEach(array, itemAdded);
                    } else {
                        if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {
                            // Compare the provided array against the previous one
                            var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; }),
                                compareOptions = {
                                    'dontLimitMoves': options['dontLimitMoves'],
                                    'sparse': true
                                };
                            editScript = ko.utils.compareArrays(lastArray, array, compareOptions);
                        }

                        for (var i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {
                            movedIndex = editScriptItem['moved'];
                            itemIndex = editScriptItem['index'];
                            switch (editScriptItem['status']) {
                                case "deleted":
                                    while (lastMappingResultIndex < itemIndex) {
                                        itemMovedOrRetained(lastMappingResultIndex++);
                                    }
                                    if (movedIndex === undefined) {
                                        mapData = lastMappingResult[lastMappingResultIndex];

                                        // Stop tracking changes to the mapping for these nodes
                                        if (mapData.dependentObservable) {
                                            mapData.dependentObservable.dispose();
                                            mapData.dependentObservable = undefined;
                                        }

                                        // Queue these nodes for later removal
                                        if (ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode).length) {
                                            if (options['beforeRemove']) {
                                                newMappingResult.push(mapData);
                                                countWaitingForRemove++;
                                                if (mapData.arrayEntry === deletedItemDummyValue) {
                                                    mapData = null;
                                                } else {
                                                    itemsForBeforeRemoveCallbacks.push(mapData);
                                                }
                                            }
                                            if (mapData) {
                                                nodesToDelete.push.apply(nodesToDelete, mapData.mappedNodes);
                                            }
                                        }
                                    }
                                    lastMappingResultIndex++;
                                    break;

                                case "added":
                                    while (currentArrayIndex < itemIndex) {
                                        itemMovedOrRetained(lastMappingResultIndex++);
                                    }
                                    if (movedIndex !== undefined) {
                                        itemsToMoveFirstIndexes.push(newMappingResult.length);
                                        itemMovedOrRetained(movedIndex);
                                    } else {
                                        itemAdded(editScriptItem['value']);
                                    }
                                    break;
                            }
                        }

                        while (currentArrayIndex < array.length) {
                            itemMovedOrRetained(lastMappingResultIndex++);
                        }

                        // Record that the current view may still contain deleted items
                        // because it means we won't be able to use a provided editScript.
                        newMappingResult['_countWaitingForRemove'] = countWaitingForRemove;
                    }

                    // Store a copy of the array items we just considered so we can difference it next time
                    ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);

                    // Call beforeMove first before any changes have been made to the DOM
                    callCallback(options['beforeMove'], itemsForMoveCallbacks);

                    // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
                    ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);

                    var i, j, lastNode, nodeToInsert, mappedNodes, activeElement;

                    // Since most browsers remove the focus from an element when it's moved to another location,
                    // save the focused element and try to restore it later.
                    try {
                        activeElement = domNode.ownerDocument.activeElement;
                    } catch(e) {
                        // IE9 throws if you access activeElement during page load (see issue #703)
                    }

                    // Try to reduce overall moved nodes by first moving the ones that were marked as moved by the edit script
                    if (itemsToMoveFirstIndexes.length) {
                        while ((i = itemsToMoveFirstIndexes.shift()) != undefined) {
                            mapData = newMappingResult[i];
                            for (lastNode = undefined; i; ) {
                                if ((mappedNodes = newMappingResult[--i].mappedNodes) && mappedNodes.length) {
                                    lastNode = mappedNodes[mappedNodes.length-1];
                                    break;
                                }
                            }
                            for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
                                ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
                            }
                        }
                    }

                    // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
                    for (i = 0; mapData = newMappingResult[i]; i++) {
                        // Get nodes for newly added items
                        if (!mapData.mappedNodes)
                            ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));

                        // Put nodes in the right place if they aren't there already
                        for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
                            ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
                        }

                        // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
                        if (!mapData.initialized && callbackAfterAddingNodes) {
                            callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
                            mapData.initialized = true;
                            lastNode = mapData.mappedNodes[mapData.mappedNodes.length - 1];     // get the last node again since it may have been changed by a preprocessor
                        }
                    }

                    // Restore the focused element if it had lost focus
                    if (activeElement && domNode.ownerDocument.activeElement != activeElement) {
                        activeElement.focus();
                    }

                    // If there's a beforeRemove callback, call it after reordering.
                    // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
                    // some sort of animation, which is why we first reorder the nodes that will be removed. If the
                    // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
                    // Perhaps we'll make that change in the future if this scenario becomes more common.
                    callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);

                    // Replace the stored values of deleted items with a dummy value. This provides two benefits: it marks this item
                    // as already "removed" so we won't call beforeRemove for it again, and it ensures that the item won't match up
                    // with an actual item in the array and appear as "retained" or "moved".
                    for (i = 0; i < itemsForBeforeRemoveCallbacks.length; ++i) {
                        itemsForBeforeRemoveCallbacks[i].arrayEntry = deletedItemDummyValue;
                    }

                    // Finally call afterMove and afterAdd callbacks
                    callCallback(options['afterMove'], itemsForMoveCallbacks);
                    callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
                }
            })();

            ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
            ko.nativeTemplateEngine = function () {
                this['allowTemplateRewriting'] = false;
            }

            ko.nativeTemplateEngine.prototype = new ko.templateEngine();
            ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;
            ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
                var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
                    templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
                    templateNodes = templateNodesFunc ? templateSource['nodes']() : null;

                if (templateNodes) {
                    return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
                } else {
                    var templateText = templateSource['text']();
                    return ko.utils.parseHtmlFragment(templateText, templateDocument);
                }
            };

            ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
            ko.setTemplateEngine(ko.nativeTemplateEngine.instance);

            ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
            (function() {
                ko.jqueryTmplTemplateEngine = function () {
                    // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
                    // doesn't expose a version number, so we have to infer it.
                    // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
                    // which KO internally refers to as version "2", so older versions are no longer detected.
                    var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
                        if (!jQueryInstance || !(jQueryInstance['tmpl']))
                            return 0;
                        // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
                        try {
                            if (jQueryInstance['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
                                // Since 1.0.0pre, custom tags should append markup to an array called "__"
                                return 2; // Final version of jquery.tmpl
                            }
                        } catch(ex) { /* Apparently not the version we were looking for */ }

                        return 1; // Any older version that we don't support
                    })();

                    function ensureHasReferencedJQueryTemplates() {
                        if (jQueryTmplVersion < 2)
                            throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
                    }

                    function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
                        return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
                    }

                    this['renderTemplateSource'] = function(templateSource, bindingContext, options, templateDocument) {
                        templateDocument = templateDocument || document;
                        options = options || {};
                        ensureHasReferencedJQueryTemplates();

                        // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
                        var precompiled = templateSource['data']('precompiled');
                        if (!precompiled) {
                            var templateText = templateSource['text']() || "";
                            // Wrap in "with($whatever.koBindingContext) { ... }"
                            templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";

                            precompiled = jQueryInstance['template'](null, templateText);
                            templateSource['data']('precompiled', precompiled);
                        }

                        var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
                        var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);

                        var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
                        resultNodes['appendTo'](templateDocument.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work

                        jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
                        return resultNodes;
                    };

                    this['createJavaScriptEvaluatorBlock'] = function(script) {
                        return "{{ko_code ((function() { return " + script + " })()) }}";
                    };

                    this['addTemplate'] = function(templateName, templateMarkup) {
                        document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
                    };

                    if (jQueryTmplVersion > 0) {
                        jQueryInstance['tmpl']['tag']['ko_code'] = {
                            open: "__.push($1 || '');"
                        };
                        jQueryInstance['tmpl']['tag']['ko_with'] = {
                            open: "with($1) {",
                            close: "} "
                        };
                    }
                };

                ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
                ko.jqueryTmplTemplateEngine.prototype.constructor = ko.jqueryTmplTemplateEngine;

                // Use this one by default *only if jquery.tmpl is referenced*
                var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
                if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
                    ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);

                ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
            })();
        }));
    }());
})();

/**
 * 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.
 */

/**
 * @api
 */
define('Magento_Ui/js/lib/registry/registry',[
    '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;
});

/**
 * 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
        };

        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.
 */

/* 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('Magento_Captcha/js/action/refresh',[
    'jquery', 'mage/url'
], function ($, urlBuilder) {
    'use strict';

    return function (refreshUrl, formId, imageSource) {
        return $.ajax({
            url: urlBuilder.build(refreshUrl),
            type: 'POST',
            data: JSON.stringify({
                'formId': formId
            }),
            global: false,
            contentType: 'application/json'
        }).done(
            function (response) {
                if (response.imgSrc) {
                    imageSource(response.imgSrc);
                }
            }
        );
    };
});

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

define('Magento_Captcha/js/model/captcha',[
    'jquery',
    'ko',
    'Magento_Captcha/js/action/refresh'
], function ($, ko, refreshAction) {
    'use strict';

    return function (captchaData) {
        return {
            formId: captchaData.formId,
            imageSource: ko.observable(captchaData.imageSrc),
            visibility: ko.observable(false),
            captchaValue: ko.observable(null),
            isRequired: ko.observable(captchaData.isRequired),
            isCaseSensitive: captchaData.isCaseSensitive,
            imageHeight: captchaData.imageHeight,
            refreshUrl: captchaData.refreshUrl,
            isLoading: ko.observable(false),
            timestamp: null,

            /**
             * @return {String}
             */
            getFormId: function () {
                return this.formId;
            },

            /**
             * @param {String} formId
             */
            setFormId: function (formId) {
                this.formId = formId;
            },

            /**
             * @return {Boolean}
             */
            getIsVisible: function () {
                return this.visibility();
            },

            /**
             * @param {Boolean} flag
             */
            setIsVisible: function (flag) {
                this.visibility(flag);
            },

            /**
             * @return {Boolean}
             */
            getIsRequired: function () {
                return this.isRequired();
            },

            /**
             * @param {Boolean} flag
             */
            setIsRequired: function (flag) {
                this.isRequired(flag);
            },

            /**
             * @return {Boolean}
             */
            getIsCaseSensitive: function () {
                return this.isCaseSensitive;
            },

            /**
             * @param {Boolean} flag
             */
            setIsCaseSensitive: function (flag) {
                this.isCaseSensitive = flag;
            },

            /**
             * @return {String|Number}
             */
            getImageHeight: function () {
                return this.imageHeight;
            },

            /**
             * @param {String|Number}height
             */
            setImageHeight: function (height) {
                this.imageHeight = height;
            },

            /**
             * @return {String}
             */
            getImageSource: function () {
                return this.imageSource;
            },

            /**
             * @param {String} imageSource
             */
            setImageSource: function (imageSource) {
                this.imageSource(imageSource);
            },

            /**
             * @return {String}
             */
            getRefreshUrl: function () {
                return this.refreshUrl;
            },

            /**
             * @param {String} url
             */
            setRefreshUrl: function (url) {
                this.refreshUrl = url;
            },

            /**
             * @return {*}
             */
            getCaptchaValue: function () {
                return this.captchaValue;
            },

            /**
             * @param {*} value
             */
            setCaptchaValue: function (value) {
                this.captchaValue(value);
            },

            /**
             * Refresh captcha.
             */
            refresh: function () {
                var refresh,
                    self = this;

                this.isLoading(true);

                refresh = refreshAction(this.getRefreshUrl(), this.getFormId(), this.getImageSource());
                $.when(refresh).done(function () {
                    self.isLoading(false);
                });
            }
        };
    };
});

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

define('Magento_Captcha/js/model/captchaList',['jquery'], function ($) {
    'use strict';

    var captchaList = [];

    return {
        /**
         * @param {Object} captcha
         */
        add: function (captcha) {
            captchaList.push(captcha);
        },

        /**
         * @param {String} formId
         * @return {Object}
         */
        getCaptchaByFormId: function (formId) {
            var captcha = null;

            $.each(captchaList, function (key, item) {
                if (formId === item.formId) {
                    captcha = item;

                    return false;
                }
            });

            return captcha;
        },

        /**
         * @return {Array}
         */
        getCaptchaList: function () {
            return captchaList;
        }
    };
});

/**
 * 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.
 */

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
         * @returns {Deferred}
         */
        post: function (url, data, global, contentType, headers) {
            headers = headers || {};
            global = global === undefined ? true : global;
            contentType = contentType || 'application/json';

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

        /**
         * 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.1 | 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 = 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 (key, 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();
            }

            key = encodeURIComponent(key)
                .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 =
                key + '=' + converter.write(value, key) + stringifiedAttributes)
        }

        function get (key) {
            if (typeof document === 'undefined' || (arguments.length && !key)) {
                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 foundKey = decodeURIComponent(parts[0]);
                    jar[foundKey] = converter.read(value, foundKey);

                    if (key === foundKey) {
                        break
                    }
                } catch (e) {}
            }

            return key ? jar[key] : jar
        }

        return Object.create(
            {
                set: set,
                get: get,
                remove: function (key, attributes) {
                    set(
                        key,
                        '',
                        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);
    };
});

/*
 * 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', 'js-cookie/cookie-wrapper'], 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 = {
            cookieLifeTime: 86400 //1 day by default
        },
        storage,
        storageInvalidation,
        invalidateCacheBySessionTimeOut,
        invalidateCacheByCloseCookieSession,
        dataProvider,
        buffer,
        customerData,
        deferred = $.Deferred();

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



    /**
     * Storage initialization
     */
    function initStorage() {
        $.cookieStorage.setConf({
            path: '/',
            expires: new Date(Date.now() + parseInt(options.cookieLifeTime, 10) * 1000),
            samesite: 'lax'
        });
        storage = $.initNamespaceStorage('mage-cache-storage').localStorage;
        storageInvalidation = $.initNamespaceStorage('mage-cache-storage-section-invalidation').localStorage;
    }

    // Initialize storage with default parameters to prevent JS errors while component still not initialized
    initStorage();

    /**
     * @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 () {
        if (!$.cookieStorage.isSet('mage-cache-sessid')) {
            $.cookieStorage.set('mage-cache-sessid', true);
            storage.removeAll();
        }
    };

    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);
             var currentUrl = window.location.pathname;

            if (sectionNames.includes('customer') || (typeof JSON.parse(localStorage.getItem('mage-cache-storage')).customer !== 'undefined'
                && JSON.parse(localStorage.getItem('mage-cache-storage')).customer.email !== '')) {
                if(!sectionNames.includes('cart')) {
                    sectionNames.push('cart')
                }
                if (!sectionNames.includes('wishlist')) {
                    sectionNames.push('wishlist', 'kemana_wishlist');
                }
            } else {
                if(sectionNames.includes('wishlist') || sectionNames.includes('kemana_wishlist')) {
                    sectionNames = sectionNames.filter(name => name !== 'wishlist' && name !== 'kemana_wishlist');
                }
            }
            if(_.isEmpty(sectionNames)) {
                return;
            }
            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) { //eslint-disable-line eqeqeq
                        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', '');
            }
        },

        /**
         * Storage init
         */
        initStorage: initStorage,

        /**
         * 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) {
                sectionData = storage.get(sectionName);

                if (typeof sectionData === 'undefined' ||
                    typeof sectionData === 'object' &&
                    cookieSectionTimestamp != sectionData['data_id'] //eslint-disable-line
                ) {
                    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) {
            return buffer.get(sectionName);
        },

        /**
         * @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) {
            var result = dataProvider.getFromServer(sectionNames, forceNewSectionTimestamp);
            if (result && typeof result.done === 'function') {
                result.done(function (sections) {
                    $(document).trigger('customer-data-reload', [sectionNames]);
                    buffer.update(sections);
                });
            }

        },


        /**
         * @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();
        },

        /**
         * @param {Object} settings
         * @constructor
         */
        'Magento_Customer/js/customer-data': function (settings) {
            options = settings;

            // re-init storage with a new settings
            customerData.initStorage();
            var sections = ['wishlist', 'kemana_wishlist'];
            customerData.invalidate(sections)
            invalidateCacheBySessionTimeOut(settings);
            invalidateCacheByCloseCookieSession();
            customerData.init();
            deferred.resolve();
        }
    };

    /**
     * Events listener
     */
    $(document).on('ajaxComplete', function (event, xhr, settings) {
        var sections,
            redirects;

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

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

                if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects)) &&
                    _.intersection(sections, ['wishlist', 'kemana_wishlist']).length === 0
                ) {   //eslint-disable-line
                    return;
                }
                customerData.reload(sections, true);
            }
        }
    });

    /**
     * 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 © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Captcha/js/view/checkout/defaultCaptcha',[
    'jquery',
    'uiComponent',
    'Magento_Captcha/js/model/captcha',
    'Magento_Captcha/js/model/captchaList',
    'Magento_Customer/js/customer-data',
    'underscore'
], function ($, Component, Captcha, captchaList, customerData, _) {
    'use strict';

    var captchaConfig;

    return Component.extend({
        defaults: {
            template: 'Magento_Captcha/checkout/captcha'
        },
        dataScope: 'global',
        currentCaptcha: null,
        subscribedFormIds: [],

        /**
         * @return {*}
         */
        captchaValue: function () {
            return this.currentCaptcha.getCaptchaValue();
        },

        /** @inheritdoc */
        initialize: function () {
            this._super();

            if (window[this.configSource] && window[this.configSource].captcha) {
                captchaConfig = window[this.configSource].captcha;
                $.each(captchaConfig, function (formId, captchaData) {
                    var captcha;

                    captchaData.formId = formId;
                    captcha = Captcha(captchaData);
                    this.checkCustomerData(formId, customerData.get('captcha')(), captcha);
                    this.subscribeCustomerData(formId, captcha);
                    captchaList.add(captcha);
                }.bind(this));
            }
        },

        /**
         * Check customer data for captcha configuration.
         *
         * @param {String} formId
         * @param {Object} captchaData
         * @param {Object} captcha
         */
        checkCustomerData: function (formId, captchaData, captcha) {
            if (!_.isEmpty(captchaData) &&
                !_.isEmpty(captchaData[formId]) &&
                captchaData[formId].timestamp > captcha.timestamp
            ) {
                if (!captcha.isRequired() && captchaData[formId].isRequired) {
                    captcha.refresh();
                }
                captcha.isRequired(captchaData[formId].isRequired);
                captcha.timestamp = captchaData[formId].timestamp;
            }
        },

        /**
         * Subscribe for customer data updates.
         *
         * @param {String} formId
         * @param {Object} captcha
         */
        subscribeCustomerData: function (formId, captcha) {
            if (this.subscribedFormIds.includes(formId) === false) {
                this.subscribedFormIds.push(formId);
                customerData.get('captcha').subscribe(function (captchaData) {
                    this.checkCustomerData(formId, captchaData, captcha);
                }.bind(this));
            }
        },

        /**
         * @return {Boolean}
         */
        getIsLoading: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.isLoading : false;
        },

        /**
         * @return {null|Object}
         */
        getCurrentCaptcha: function () {
            return this.currentCaptcha;
        },

        /**
         * @param {Object} captcha
         */
        setCurrentCaptcha: function (captcha) {
            this.currentCaptcha = captcha;
        },

        /**
         * @return {String|null}
         */
        getFormId: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getFormId() : null;
        },

        /**
         * @return {Boolean}
         */
        getIsVisible: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getIsVisible() : false;
        },

        /**
         * @param {Boolean} flag
         */
        setIsVisible: function (flag) {
            this.currentCaptcha.setIsVisible(flag);
        },

        /**
         * @return {Boolean}
         */
        isRequired: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getIsRequired() : false;
        },

        /**
         * Set isRequired on current captcha model.
         *
         * @param {Boolean} flag
         */
        setIsRequired: function (flag) {
            this.currentCaptcha.setIsRequired(flag);
        },

        /**
         * @return {Boolean}
         */
        isCaseSensitive: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getIsCaseSensitive() : false;
        },

        /**
         * @return {String|Number|null}
         */
        imageHeight: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getImageHeight() : null;
        },

        /**
         * @return {String|null}
         */
        getImageSource: function () {
            return this.currentCaptcha !== null ? this.currentCaptcha.getImageSource() : null;
        },

        /**
         * Refresh captcha.
         */
        refresh: function () {
            this.currentCaptcha.refresh();
        }
    });
});

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

/**
 * @api
 */
define('Magento_Ui/js/model/messages',[
    'ko',
    'uiClass'
], function (ko, Class) {
    'use strict';

    return Class.extend({
        /** @inheritdoc */
        initialize: function () {
            this._super()
                .initObservable();

            return this;
        },

        /** @inheritdoc */
        initObservable: function () {
            this.errorMessages = ko.observableArray([]);
            this.successMessages = ko.observableArray([]);

            return this;
        },

        /**
         * Add  message to list.
         * @param {Object} messageObj
         * @param {Object} type
         * @returns {Boolean}
         */
        add: function (messageObj, type) {
            var expr = /([%])\w+/g,
                message;

            if (!messageObj.hasOwnProperty('parameters')) {
                this.clear();
                type.push(messageObj.message);

                return true;
            }
            message = messageObj.message.replace(expr, function (varName) {
                varName = varName.substr(1);

                if (!isNaN(varName)) {
                    varName--;
                }

                if (messageObj.parameters.hasOwnProperty(varName)) {
                    return messageObj.parameters[varName];
                }

                return messageObj.parameters.shift();
            });
            this.clear();
            type.push(message);

            return true;
        },

        /**
         * Add success message.
         *
         * @param {Object} message
         * @return {*|Boolean}
         */
        addSuccessMessage: function (message) {
            return this.add(message, this.successMessages);
        },

        /**
         * Add error message.
         *
         * @param {Object} message
         * @return {*|Boolean}
         */
        addErrorMessage: function (message) {
            return this.add(message, this.errorMessages);
        },

        /**
         * Get error messages.
         *
         * @return {Array}
         */
        getErrorMessages: function () {
            return this.errorMessages;
        },

        /**
         * Get success messages.
         *
         * @return {Array}
         */
        getSuccessMessages: function () {
            return this.successMessages;
        },

        /**
         * Checks if an instance has stored messages.
         *
         * @return {Boolean}
         */
        hasMessages: function () {
            return this.errorMessages().length > 0 || this.successMessages().length > 0;
        },

        /**
         * Removes stored messages.
         */
        clear: function () {
            this.errorMessages.removeAll();
            this.successMessages.removeAll();
        }
    });
});

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

define('Magento_Ui/js/model/messageList',[
    './messages'
], function (Messages) {
    'use strict';

    return new Messages();
});

/**
 * 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;

            if (typeof fn === 'object') {
                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);
                }
            }
            // Init module in separate task to prevent blocking main thread.
            setTimeout(fn);
        }, 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;
});

/**
 * @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;
});

define('text!js-translation.json',[],function () { return '{"* Required Fields":"* Wajib Diisi","- 3 payments with 0% interest":"- Bunga 0% untuk 3x cicilan","1 month later":"1 bulan kemudian","2 months later":"2 bulan kemudian","3 months later":"3 bulan kemudian","30 days later":"1 bulan kemudian","60 days later":"2 bulan kemudian","Add to Cart":"Tambah Ke Keranjang","Adding...":"Menambahkan...","Apply":"Gunakan","Apply Discount":"GUNAKAN","Apply Discount Code":"Gunakan kode diskon","Apply Filters":"Cari","Are you sure you want to delete this address?":"Apakah anda yakin untuk menghapus alamat ini?","Are you sure you would like to remove this item from the shopping cart?":"Apakah Anda ingin menghapus barang ini dari keranjang?","Available in %1 stores":"Tersedia di Toko %1","Back":"Kembali","Bank & Installment Plan":"Bank dan Tenor Cicilan","Billing Address":"Alamat penagihan","Browse All Products":"Jelajahi Semua Produk","Buy Now":"Beli Sekarang","Cancel":"Tidak","Cancel Coupon":"Batalkan kupon","Cancel coupon":"Batalkan kupon","Carrier Title":"Carrier","Cart Subtotal":"Subtotal Belanja","Check out faster":"Proses pembayaran lebih cepat","Checkout as a new customer":"BUAT PESANAN SEBAGAI PELANGGAN BARU","Checkout out using your account":"Checkout dengan menggunakan akun anda","Checkout using your account":"BUAT PESANAN MENGGUNAKAN AKUN ANDA","Choose Bank & Installment":"Pilih Bank dan Tenor Cicilan","Choose Your Size":"PANDUAN UKURAN","Choose your payment method":"Pilih metode pembayaran Anda","City":"Kota","Click and Collect":"Klik & Ambil di Toko","Collect your orders at our stores":"Ambil order anda di toko Kami","Confirm":"Kembali","Confirm Password":"Konfirmasi Kata Sandi","Create Account":"Buat Akun","Create an Account":"Buat Akun","Creating an account has many benefits:":"Dengan membuat akun, anda akan mendapat kelebihan:","Credit Card Number":"Nomor Kartu Kredit","Credit card number cannot be empty":"Nomor kartu kredit harus diisi","Delete":"Hapus","Discount":"Diskon","Discount Code":"Kode Diskon","District":"Kecamatan","EDS Code":"EDS Kode","EDS Code must be filled.":"Kode EDS harus diisi.","EDS Not Eligible":"EDS tidak berlaku","EDS Successfully applied.":"Kode EDS berhasil digunakan.","EDS application is cancelled.":"Penggunaan EDS telah dibatalkan","EDS discount has been successfully applied to selected items.":"Diskon EDS berhasil digunakan untuk produk-produk yang berlaku","EDS discount has successfully been applied!":"EDS telah berhasil digunakan","ENTER YOUR PERSONAL DETAILS":"MASUKKAN DATA PRIBADI ANDA","Earn and Burn MAPCLUB Points":"Earn dan Burn point MAPCLUB","Edit":"Ubah","Edit Location":"Edit lokasi","Email Address":"Email Anda","Enter discount code":"Masukkan kode diskon","Enter your email address":"Masukkan Alamat Email  Anda","Enter your password":"Masukan password Anda","Estimate Shipping and Tax":"Estimasi pengiriman dan pajak","Estimate Tax":"Estimasi pajak","Estimated Total":"Total Estimasi","Expiration Date":"Tanggal Berlaku","Expiration Month":"Bulan Berlaku","Expiration Year":"Tahun Berlaku","Expiry Month\\/Year":"Bulan\\/Tahun Kadaluwarsa","FREE SHIPPING":"PENGIRIMAN GRATIS","Filter & Sort":"Saring & Urutkan","Filters":"Saring","Find out all about EDS":"Cari tahu tentang EDS","First Name":"Nama Depan","First instalment":"Cicilan pertama","Forgot Your Password?":"Lupa Kata Sandi?","Gift options":"Gift Options","Go to Checkout":"Lanjut ke Checkout","I Understand":"Saya Mengerti","In stock":"dalam stok","Input Discount Code":"Masukkan Kode Diskon","Input EDS Code":"Masukkan EDS Kode","Item in Cart":"Produk di keranjang belanja","Items in Cart":"Produk di keranjang belanja","Last Name":"Nama Belakang","Less than":"Kurang dari","Letters only please":"Silakan masukkan huruf saja","Letters, numbers, spaces or underscores only please":"Hanya huruf, angka, spasi atau garis bawah","Loading...":"Memuat\\ufffd","Locate on map":"Cari di peta","Login":"Masuk","Method Title":"Metode","Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.":"Minimum jumlah pengisian kata sandi harus sama atau lebih dari %1 karakter.","Month":"Bulan","My Cart":"Keranjang","My billing and shipping address are the same":"Alamat penagihan dan pengiriman saya sama","New Address":"Tambah Alamat","Next":"Lanjutkan","No Password":"Kata Sandi Kosong","No Payment method available.":"Tidak ada metode pembayaran yang tersedia","No records found.":"Tidak ada catatan ditemukan","No results found":"Tidak ada hasil ditemukan","Not a member?":"Belum menjadi member?","Not yet calculated":"Belum terhitung","OK":"Ya","Order Summary":"Ringkasan Belanja","Order confirmation email would be sent to this address. You can create an account after checkout.":"Konfirmasi pesanan akan dikirimkan ke alamat email ini. Anda dapat membuat akun setelah menyelesaikan pembayaran.","Out of stock":"Habis","PLACE ORDER":"Buat Pesanan","Password":"Kata Sandi","Payment Details":"Detail Pembayaran","Payment Method":"Metode Pembayaran","Phone Number":"Nomor Telepon","Pinpoint Location":"Tempatkan lokasi","Pinpoint location on map for Grab delivery availability":"Tempatkan lokasi di peta untuk ketersediaan Pengiriman Grab","Please choose bank & installment plan":"Pilih Bank dan Tenor Cicilan","Please choose payment method first":"Silakan pilih metode pembayaran dan lengkapi pengisian","Please enter a valid email address (Ex: johndoe@domain.com).":"Silakan masukkan alamat email Anda dengan benar (Contoh: johndoe@domain.com).","Please enter a valid number in this field.":"Masukkan nomor yang valid pada kolom ini.","Please enter less or equal than %1 symbols.":"Silakan masukkan %1 angka.","Please enter less or equal than {0} symbols.":"Silakan masukkan {0} angka.","Please enter more or equal than %1 symbols.":"Silakan masukkan %1 angka.","Please enter more or equal than {0} symbols.":"Silakan masukkan {0} angka.","Please enter the same value again.":"Silahkan masukan nilai yang sama kembali","Please make sure phone number starts with 8 and ranges between 8-12 digits.":"Silakan pastikan nomor telepon dimulai dari angka 8 dan berkisar 8 \\u2013 12 angka","Please provide the details of the person collecting the order. We will send the email when the order is ready to collect.":"Lengkapi Detail Kontak yang akan mengambil pesanan. Kami akan mengirimkan email ketika pesanan siap untuk diambil.","Please select a city.":"Silakan pilih Kota","Please select a district.":"Silakan pilih Kecamatan","Points available for redemption":"Point yang tersedia untuk Redeem","Price":"Harga","Proceed to Checkout":"Lanjut ke Checkout","Provided Zip\\/Postal Code seems to be invalid.":"Kode Pos tidak valid","Qty":"Jumlah","Recently added item(s)":"Produk terbaru yang ditambah ","Redeem points (= Rp. %1)":"Poin yang Ditukarkan (= Rp. %1)","Remarks for drivers":"Catatan untuk pengemudi","Remove":"Hapus","Remove item":"Hapus","Reset":"Ulang\\/ Diulang","Review & Payments":"Pembayaran","SUCCESSFULLY ADDED":"SUKSES DITAMBAHKAN","Save":"Simpan","Save Address":"Simpan","Save in address book":"Simpan di buku alamat","Search":"Cari","Search bank issuer":"Cari Bank","See Balance":"Lihat Saldo","See Details":"Lihat Detail","See order and shipping status":"Melihat status order dan pengiriman","Select Delivery Method":"Pilih Metode Pengiriman","Select Method":"Pilih Metode","Ship To:":"Kirim ke","Shipping":"Pengiriman","Shipping Address":"Alamat pengiriman","Shipping Method":"Metode Pengiriman","Shipping Method:":"Metode Pengiriman","Shipping Methods":"Metode Pengiriman","Showing <strong>%s<\\/strong> Results":"Tunjukkan <strong>%s<\\/strong> Hasil","Sign In":"Masuk | Daftar","Sign Up for Newsletter":"Berlangganan newsletter untuk info terbaru","Sign in to loyalty account":"Masuk ke MAPCLUB akun","Sign in with your account, or continue as guest.":"Masuk dengan akun, atau lanjutkan pemesanan sebagai tamu.","Size Chart":"Panduan Ukuran","Sorry, no quotes are available for this order at this time":"Maaf,\\ntidak ada quotation yang tersedia untuk order ini sekarang","Sorry, please choose another delivery method. Your cart has exceeded the maximum weight possible for Grab Delivery.":"maaf silahkan gunakan metoda pengiriman lain. Keranjang Anda sudah melebihi maksimum berat untuk Pengiriman Grab.","Strong":"Kuat","Submit":"Masuk","Template is not loading":"Template tidak dapat dimuat","The Date of Birth should be %1 years old.":"Persyaratan usia minimal harus berusia %1 tahun.","The credit card number is invalid \\u2013 please try another credit card number or change the issuer bank":"Nomor kartu kredit yang dimasukkan tidak sesuai, mohon pastikan nomor kartu kredit yang dimasukkan dan bank yang dipilih benar","The minimum amount should be S$30.00":"Transaksi minimum [[minimum_spend]]","The product is reserved for you for the next 10 minutes. Complete the purchase within this time frame if you do not want to risk losing it!":"Produk tersedia dalam waktu 10 menit ke depan. Selesaikan pembelian jika Anda tidak ingin  mengalami resiko kehilangan produk tersebut!","This is a required field.":"Wajib Diisi","Three interest-free payments totalling":"3x Cicilan bebas bunga dengan total keseluruhan tagihan","Track order history":"Melacak order lampau","Type in the city or postcode":"Ketika di Kota atau Kode Pos","Update":"Perbarui","Use Now":"Gunakan Sekarang","Use maximum points":"Gunakan Point Maksimal","Use my current location":"Gunakan Lokasi Saya Sekarang","Use this Location":"Gunakan lokasi ini","Very Strong":"Sangat Kuat","View Details":"Lihat Detail","View and edit cart":"Periksa Keranjang Belanja","View cart":"Lihat Keranjang","WEIGHT LIMIT EXCEEDED!":"Batas berat terlampaui!","We deliver your order to your home":"Kami kirimkan order anda ke rumah","Weak":"Lemah","What is this?":"CVV adalah kode 3 angka yang terdapat di balik kartu debit\\/kredit Anda dan dapat digunakan sebagai bukti bahwa Anda adalah pemiliki kartu debit\\/kredit tersebut.","Year":"Tahun","You already have an account with us. Sign in or continue as guest.":"Anda telah memiliki akun. Silakan sign in atau lanjutkan sebagai tamu.","You can buy this product only in quantities of %1 at a time.":"Anda dapat membeli produk ini hanya dalam kuantitas %1 sekaligus","You can create an account after checkout.":"Anda dapat membuat akun setelah checkout.","You can track your order status by creating an account.":"Anda dapat melacak status pembeli dengan membuat akun.","You have no items in your shopping cart.":"Keranjang belanja saat ini kosong","You will be redirected to Atome when you proceed to checkout. ":"Kamu akan diarahkan ke halaman ATOME untuk melanjutkan pembayaran.","Your area is not serviceable by Grab currently. Please check again later.":"Saat ini area Anda belum dapat dilayani oleh Grab. Harap periksa kembali nanti.","edit":"Ubah","from":"Dari","items left":"item yang tersisa","or":"\\/","to":"Untuk","type in the city or postcode":"Ketik nama kota atau kode pos","use my current location":" Gunakan lokasi saya saat ini"}';});

/**
 * 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 © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Customer/js/action/login',[
    'jquery',
    'mage/storage',
    'Magento_Ui/js/model/messageList',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function ($, storage, globalMessageList, customerData, $t) {
    'use strict';

    var callbacks = [],

        /**
         * @param {Object} loginData
         * @param {String} redirectUrl
         * @param {*} isGlobal
         * @param {Object} messageContainer
         */
        action = function (loginData, redirectUrl, isGlobal, messageContainer) {
            messageContainer = messageContainer || globalMessageList;
            let customerLoginUrl = 'customer/ajax/login';

            if (loginData.customerLoginUrl) {
                customerLoginUrl = loginData.customerLoginUrl;
                delete loginData.customerLoginUrl;
            }

            return storage.post(
                customerLoginUrl,
                JSON.stringify(loginData),
                isGlobal
            ).done(function (response) {
                if (response.errors) {
                    messageContainer.addErrorMessage(response);
                    callbacks.forEach(function (callback) {
                        callback(loginData);
                    });
                } else {
                    callbacks.forEach(function (callback) {
                        callback(loginData);
                    });
                    customerData.invalidate(['customer']);

                    if (response.redirectUrl) {
                        window.location.href = response.redirectUrl;
                    } else if (redirectUrl) {
                        window.location.href = redirectUrl;
                    } else {
                        location.reload();
                    }
                }
            }).fail(function () {
                messageContainer.addErrorMessage({
                    'message': $t('Could not authenticate. Please try again later')
                });
                callbacks.forEach(function (callback) {
                    callback(loginData);
                });
            });
        };

    /**
     * @param {Function} callback
     */
    action.registerLoginCallback = function (callback) {
        callbacks.push(callback);
    };

    return action;
});

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

define('Magento_Captcha/js/view/checkout/loginCaptcha',[
    'Magento_Captcha/js/view/checkout/defaultCaptcha',
    'Magento_Captcha/js/model/captchaList',
    'Magento_Customer/js/action/login',
    'underscore'
],
function (defaultCaptcha, captchaList, loginAction, _) {
    'use strict';

    return defaultCaptcha.extend({
        /** @inheritdoc */
        initialize: function () {
            var self = this,
                currentCaptcha;

            this._super();
            currentCaptcha = captchaList.getCaptchaByFormId(this.formId);

            if (currentCaptcha != null) {
                currentCaptcha.setIsVisible(true);
                this.setCurrentCaptcha(currentCaptcha);

                loginAction.registerLoginCallback(function (loginData) {
                    if (loginData['captcha_form_id'] &&
                        loginData['captcha_form_id'] === self.formId &&
                        self.isRequired()
                    ) {
                        _.defer(self.refresh.bind(self));
                    }
                });
            }
        }
    });
});


/*! jRespond.js v 0.10 | Author: Jeremy Fields [jeremy.fields@viget.com], 2013 | License: MIT */
!function(a,b,c){"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=c:(a[b]=c,"function"==typeof define&&define.amd&&define(b,[],function(){return c}))}(this,"jRespond",function(a,b,c){"use strict";return function(a){var b=[],d=[],e=a,f="",g="",i=0,j=100,k=500,l=k,m=function(){var a=0;return a="number"!=typeof window.innerWidth?0!==document.documentElement.clientWidth?document.documentElement.clientWidth:document.body.clientWidth:window.innerWidth},n=function(a){if(a.length===c)o(a);else for(var b=0;b<a.length;b++)o(a[b])},o=function(a){var e=a.breakpoint,h=a.enter||c;b.push(a),d.push(!1),r(e)&&(h!==c&&h.call(null,{entering:f,exiting:g}),d[b.length-1]=!0)},p=function(){for(var a=[],e=[],h=0;h<b.length;h++){var i=b[h].breakpoint,j=b[h].enter||c,k=b[h].exit||c;"*"===i?(j!==c&&a.push(j),k!==c&&e.push(k)):r(i)?(j===c||d[h]||a.push(j),d[h]=!0):(k!==c&&d[h]&&e.push(k),d[h]=!1)}for(var l={entering:f,exiting:g},m=0;m<e.length;m++)e[m].call(null,l);for(var n=0;n<a.length;n++)a[n].call(null,l)},q=function(a){for(var b=!1,c=0;c<e.length;c++)if(a>=e[c].enter&&a<=e[c].exit){b=!0;break}b&&f!==e[c].label?(g=f,f=e[c].label,p()):b||""===f||(f="",p())},r=function(a){if("object"==typeof a){if(a.join().indexOf(f)>=0)return!0}else{if("*"===a)return!0;if("string"==typeof a&&f===a)return!0}},s=function(){var a=m();a!==i?(l=j,q(a)):l=k,i=a,setTimeout(s,l)};return s(),{addFunc:function(a){n(a)},getBreakpoint:function(){return f}}}}(this,this.document));


define("WeltPixel_DesignElements/js/canvas/jRespond", function(){});

( function( factory ) {
    "use strict";

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

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

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

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

    return $.ui.version = "1.13.2";

} );

/*!
 * 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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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
    };

} );

( function( factory ) {
	"use strict";

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

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

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

return $.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;
};

} );

/*!
 * 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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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" );
                }
            } );
        }
    } );

} );

/*!
 * 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/

( function( factory ) {
    "use strict";

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

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

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

    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();
                } );
            }
        };
    } );

    return $.widget;

} );

/*!
 * 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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/tabs',[
            "jquery",
            "../keycode",
            "../safe-active-element",
            "../unique-id",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.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" );
            }
        } );
    }

    return $.ui.tabs;

} );

define('WeltPixel_DesignElements/js/toggles_accordions_tabs',['jquery', 'jRespond', 'jquery-ui-modules/tabs'], function ($) {
    "use strict";

    var SEMICOLONTABS = SEMICOLONTABS || {};

    SEMICOLONTABS.widget = {
        init: function () {
            SEMICOLONTABS.widget.tabs();
            SEMICOLONTABS.widget.tabsJustify();
            SEMICOLONTABS.widget.toggles();
            SEMICOLONTABS.widget.accordions();
        },
        tabs: function () {
            var $tabs = $('.tabs:not(.customjs)');
            if ($tabs.length > 0) {
                $tabs.each(function () {
                    var element = $(this),
                            elementSpeed = element.attr('data-speed'),
                            tabActive = element.attr('data-active');

                    if (!elementSpeed) {
                        elementSpeed = 400;
                    }
                    if (!tabActive) {
                        tabActive = 0;
                    } else {
                        tabActive = tabActive - 1;
                    }

                    $.ui.tabs({
                        active: Number(tabActive),
                        show: {
                            effect: "fade",
                            duration: Number(elementSpeed)
                        }
                    }, element);
                });
            }
        },
        tabsJustify: function () {
            if (!$('body').hasClass('device-xxs') && !$('body').hasClass('device-xs')) {
                var $tabsJustify = $('.tabs.tabs-justify');
                if ($tabsJustify.length > 0) {
                    $tabsJustify.each(function () {
                        var element = $(this),
                                elementTabs = element.find('.tab-nav > li'),
                                elementTabsNo = elementTabs.length,
                                elementContainer = 0,
                                elementWidth = 0;

                        if (element.hasClass('tabs-bordered') || element.hasClass('tabs-bb')) {
                            elementContainer = element.find('.tab-nav').outerWidth();
                        } else {
                            if (element.find('tab-nav').hasClass('tab-nav2')) {
                                elementContainer = element.find('.tab-nav').outerWidth() - (elementTabsNo * 10);
                            } else {
                                elementContainer = element.find('.tab-nav').outerWidth() - 30;
                            }
                        }

                        elementWidth = Math.floor(elementContainer / elementTabsNo);
                        elementTabs.css({'width': elementWidth + 'px'});

                    });
                }
            } else {
                $('.tabs.tabs-justify').find('.tab-nav > li').css({'width': 'auto'});
            }
        },
        toggles: function () {
            var $toggle = $('.toggle');
            if ($toggle.length > 0) {
                $toggle.each(function () {
                    var element = $(this),
                            elementState = element.attr('data-state');

                    if (elementState != 'open') {
                        element.find('.togglec').hide();
                    } else {
                        element.find('.togglet').addClass("toggleta");
                    }

                    element.find('.togglet').click(function () {
                        $(this).toggleClass('toggleta').next('.togglec').slideToggle(300);
                        return true;
                    });
                });
            }
        },
        accordions: function () {
            var $accordionEl = $('.accordion');
            if ($accordionEl.length > 0) {
                $accordionEl.each(function () {
                    var element = $(this),
                            elementState = element.attr('data-state'),
                            accordionActive = element.attr('data-active');

                    if (!accordionActive) {
                        accordionActive = 0;
                    } else {
                        accordionActive = accordionActive - 1;
                    }

                    element.find('.acc_content').hide();

                    if (elementState != 'closed') {
                        element.find('.acctitle:eq(' + Number(accordionActive) + ')').addClass('acctitlec').next().show();
                    }

                    element.find('.acctitle').click(function () {
                        if ($(this).next().is(':hidden')) {
                            element.find('.acctitle').removeClass('acctitlec').next().slideUp("normal");
                            $(this).toggleClass('acctitlec').next().slideDown("normal");
                        }
                        return false;
                    });
                });
            }
        }

    };

    return SEMICOLONTABS;
});

/* ========================================================================
 * Bootstrap: dropdown.js v3.3.6
 * http://getbootstrap.com/javascript/#dropdowns
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

define('WeltPixel_DesignElements/js/bootstrap/dropdown',["jquery"], // Require jquery
    function($){

+function ($) {
    'use strict';

    // DROPDOWN CLASS DEFINITION
    // =========================

    var backdrop = '.dropdown-backdrop'
    var toggle   = '[data-toggle="dropdown"]'
    var Dropdown = function (element) {
        $(element).on('click.bs.dropdown', this.toggle)
    }

    Dropdown.VERSION = '3.3.6'

    function getParent($this) {
        var selector = $this.attr('data-target')

        if (!selector) {
            selector = $this.attr('href')
            selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
        }

        var $parent = selector && $(selector)

        return $parent && $parent.length ? $parent : $this.parent()
    }

    function clearMenus(e) {
        if (e && e.which === 3) return
        $(backdrop).remove()
        $(toggle).each(function () {
            var $this         = $(this)
            var $parent       = getParent($this)
            var relatedTarget = { relatedTarget: this }

            if (!$parent.hasClass('open')) return

            if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return

            $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))

            if (e.isDefaultPrevented()) return

            $this.attr('aria-expanded', 'false')
            $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
        })
    }

    Dropdown.prototype.toggle = function (e) {
        var $this = $(this)

        if ($this.is('.disabled, :disabled')) return

        var $parent  = getParent($this)
        var isActive = $parent.hasClass('open')

        clearMenus()

        if (!isActive) {
            if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
                // if mobile we use a backdrop because click events don't delegate
                $(document.createElement('div'))
                    .addClass('dropdown-backdrop')
                    .insertAfter($(this))
                    .on('click', clearMenus)
            }

            var relatedTarget = { relatedTarget: this }
            $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))

            if (e.isDefaultPrevented()) return

            $this
                .trigger('focus')
                .attr('aria-expanded', 'true')

            $parent
                .toggleClass('open')
                .trigger($.Event('shown.bs.dropdown', relatedTarget))
        }

        return false
    }

    Dropdown.prototype.keydown = function (e) {
        if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return

        var $this = $(this)

        e.preventDefault()
        e.stopPropagation()

        if ($this.is('.disabled, :disabled')) return

        var $parent  = getParent($this)
        var isActive = $parent.hasClass('open')

        if (!isActive && e.which != 27 || isActive && e.which == 27) {
            if (e.which == 27) $parent.find(toggle).trigger('focus')
            return $this.trigger('click')
        }

        var desc = ' li:not(.disabled):visible a'
        var $items = $parent.find('.dropdown-menu' + desc)

        if (!$items.length) return

        var index = $items.index(e.target)

        if (e.which == 38 && index > 0)                 index--         // up
        if (e.which == 40 && index < $items.length - 1) index++         // down
        if (!~index)                                    index = 0

        $items.eq(index).trigger('focus')
    }


    // DROPDOWN PLUGIN DEFINITION
    // ==========================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this)
            var data  = $this.data('bs.dropdown')

            if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
            if (typeof option == 'string') data[option].call($this)
        })
    }

    var old = $.fn.dropdown

    $.fn.dropdown             = Plugin
    $.fn.dropdown.Constructor = Dropdown


    // DROPDOWN NO CONFLICT
    // ====================

    $.fn.dropdown.noConflict = function () {
        $.fn.dropdown = old
        return this
    }


    // APPLY TO STANDARD DROPDOWN ELEMENTS
    // ===================================

    $(document)
        .on('click.bs.dropdown.data-api', clearMenus)
        .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
        .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
        .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
        .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)

}(jQuery);
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('Magento_Ui/js/lib/spinner',[
    'jquery'
], function ($) {
    'use strict';

    var selector = '[data-role="spinner"]',
        spinner = $(selector);

    return {
        /**
         * Show spinner.
         */
        show: function () {
            spinner.show();
        },

        /**
         * Hide spinner.
         */
        hide: function () {
            spinner.hide();
        },

        /**
         * Get spinner by selector.
         *
         * @param {String} id
         * @return {jQuery}
         */
        get: function (id) {
            return $(selector + '[data-component="' + id + '"]');
        }
    };
});

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

define('Magento_PaypalCaptcha/js/model/skipRefreshCaptcha',['ko'], function (ko) {
    'use strict';

    return {
        skip: ko.observable(false)
    };
});

/**
 * @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;
});


!function(){if(!window.klaviyo){window._klOnsite=window._klOnsite||[];try{window.klaviyo=new Proxy({},{get:function(n,i){return"push"===i?function(){var n;(n=window._klOnsite).push.apply(n,arguments)}:function(){for(var n=arguments.length,o=new Array(n),w=0;w<n;w++)o[w]=arguments[w];var t="function"==typeof o[o.length-1]?o.pop():void 0,e=new Promise((function(n){window._klOnsite.push([i].concat(o,[function(i){t&&t(i),n(i)}]))}));return e}}})}catch(n){window.klaviyo=window.klaviyo||[],window.klaviyo.push=function(){var n;(n=window._klOnsite).push.apply(n,arguments)}}}}();

define('Klaviyo_Reclaim/js/customer',[
    'underscore',
    'Magento_Customer/js/customer-data',
    'domReady!'
], function (_, customerData) {
    'use strict';

    var klaviyo = window.klaviyo || [];

    customerData.getInitCustomerData().done(function () {
        var customer = customerData.get('customer')();

        klaviyo.isIdentified().then((identified)=> {
            if(_.has(customer, 'email') && customer.email && !identified) {
                klaviyo.identify({
                    '$email': customer.email,
                    '$first_name': _.has(customer, 'firstname') ? customer.firstname : '',
                    '$last_name':  _.has(customer, 'lastname') ? customer.lastname : ''
                });
            }
        });

    });

});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/view/utils/dom-observer',[
    'jquery',
    'underscore',
    'domReady!'
], function ($, _) {
    'use strict';

    var counter = 1,
        watchers,
        globalObserver,
        disabledNodes = [];

    watchers = {
        selectors: {},
        nodes: {}
    };

    /**
     * Checks if node represents an element node (nodeType === 1).
     *
     * @param {HTMLElement} node
     * @returns {Boolean}
     */
    function isElementNode(node) {
        return node.nodeType === 1;
    }

    /**
     * Extracts all child descendant
     * elements of a specified node.
     *
     * @param {HTMLElement} node
     * @returns {Array}
     */
    function extractChildren(node) {
        var children = node.querySelectorAll('*');

        return _.toArray(children);
    }

    /**
     * Extracts node identifier. If ID is not specified,
     * then it will be created for the provided node.
     *
     * @param {HTMLElement} node
     * @returns {Number}
     */
    function getNodeId(node) {
        var id = node._observeId;

        if (!id) {
            id = node._observeId = counter++;
        }

        return id;
    }

    /**
     * Invokes callback passing node to it.
     *
     * @param {HTMLElement} node
     * @param {Object} data
     */
    function trigger(node, data) {
        var id = getNodeId(node),
            ids = data.invoked;

        if (_.contains(ids, id)) {
            return;
        }

        data.callback(node);
        data.invoked.push(id);
    }

    /**
     * Adds node to the observer list.
     *
     * @param {HTMLElement} node
     * @returns {Object}
     */
    function createNodeData(node) {
        var nodes   = watchers.nodes,
            id      = getNodeId(node);

        nodes[id] = nodes[id] || {};

        return nodes[id];
    }

    /**
     * Returns data associated with a specified node.
     *
     * @param {HTMLElement} node
     * @returns {Object|Undefined}
     */
    function getNodeData(node) {
        var nodeId = node._observeId;

        return watchers.nodes[nodeId];
    }

    /**
     * Removes data associated with a specified node.
     *
     * @param {HTMLElement} node
     */
    function removeNodeData(node) {
        var nodeId = node._observeId;

        delete watchers.nodes[nodeId];
    }

    /**
     * Adds removal listener for a specified node.
     *
     * @param {HTMLElement} node
     * @param {Object} data
     */
    function addRemovalListener(node, data) {
        var nodeData = createNodeData(node);

        (nodeData.remove = nodeData.remove || []).push(data);
    }

    /**
     * Adds listener for the nodes which matches specified selector.
     *
     * @param {String} selector - CSS selector.
     * @param {Object} data
     */
    function addSelectorListener(selector, data) {
        var storage = watchers.selectors;

        (storage[selector] = storage[selector] || []).push(data);
    }

    /**
     * Calls handlers associated with an added node.
     * Adds listeners for the node removal.
     *
     * @param {HTMLElement} node - Added node.
     */
    function processAdded(node) {
        _.each(watchers.selectors, function (listeners, selector) {
            listeners.forEach(function (data) {
                if (!data.ctx.contains(node) || !$(node, data.ctx).is(selector)) {
                    return;
                }

                if (data.type === 'add') {
                    trigger(node, data);
                } else if (data.type === 'remove') {
                    addRemovalListener(node, data);
                }
            });
        });
    }

    /**
     * Calls handlers associated with a removed node.
     *
     * @param {HTMLElement} node - Removed node.
     */
    function processRemoved(node) {
        var nodeData    = getNodeData(node),
            listeners   = nodeData && nodeData.remove;

        if (!listeners) {
            return;
        }

        listeners.forEach(function (data) {
            trigger(node, data);
        });

        removeNodeData(node);
    }

    /**
     * Removes all non-element nodes from provided array
     * and appends to it descendant elements.
     *
     * @param {Array} nodes
     * @returns {Array}
     */
    function formNodesList(nodes) {
        var result = [],
            children;

        nodes = _.toArray(nodes).filter(isElementNode);

        nodes.forEach(function (node) {
            result.push(node);

            children = extractChildren(node);
            result   = result.concat(children);
        });

        return result;
    }

    /**
     * Collects all removed and added nodes from
     * mutation records into separate arrays
     * while removing duplicates between both types of changes.
     *
     * @param {Array} mutations - An array of mutation records.
     * @returns {Object} Object with 'removed' and 'added' nodes arrays.
     */
    function formChangesLists(mutations) {
        var removed = [],
            added = [];

        mutations.forEach(function (record) {
            removed = removed.concat(_.toArray(record.removedNodes));
            added   = added.concat(_.toArray(record.addedNodes));
        });

        removed = removed.filter(function (node) {
            var addIndex = added.indexOf(node),
                wasAdded = !!~addIndex;

            if (wasAdded) {
                added.splice(addIndex, 1);
            }

            return !wasAdded;
        });

        return {
            removed: formNodesList(removed),
            added: formNodesList(added)
        };
    }

    /**
     * Verify if the DOM node is a child of a defined disabled node, if so we shouldn't observe provided mutation.
     *
     * @param {Object} mutation - a single mutation
     * @returns {Boolean}
     */
    function shouldObserveMutation(mutation) {
        var isDisabled;

        if (disabledNodes.length > 0) {
            // Iterate through the disabled nodes and determine if this mutation is occurring inside one of them
            isDisabled = _.find(disabledNodes, function (node) {
                return node === mutation.target || $.contains(node, mutation.target);
            });

            // If we find a matching node we should not observe the mutation
            return !isDisabled;
        }

        return true;
    }

    /**
     * Should we observe these mutations? Check the first and last mutation to determine if this is a disabled mutation,
     * we check both the first and last in case one has been removed from the DOM during the process of the mutation.
     *
     * @param {Array} mutations - An array of mutation records.
     * @returns {Boolean}
     */
    function shouldObserveMutations(mutations) {
        var firstMutation,
            lastMutation;

        if (mutations.length > 0) {
            firstMutation = mutations[0];
            lastMutation = mutations[mutations.length - 1];

            return shouldObserveMutation(firstMutation) && shouldObserveMutation(lastMutation);
        }

        return true;
    }

    globalObserver = new MutationObserver(function (mutations) {
        var changes;

        if (shouldObserveMutations(mutations)) {
            changes = formChangesLists(mutations);

            changes.removed.forEach(processRemoved);
            changes.added.forEach(processAdded);
        }
    });

    globalObserver.observe(document.body, {
        subtree: true,
        childList: true
    });

    return {
        /**
         * Disable a node from being observed by the mutations, you may want to disable specific aspects of the
         * application which are heavy on DOM changes. The observer running on some actions could cause significant
         * delays and degrade the performance of that specific part of the application exponentially.
         *
         * @param {HTMLElement} node - a HTML node within the document
         */
        disableNode: function (node) {
            disabledNodes.push(node);
        },

        /**
         * Adds listener for the appearance of nodes that matches provided
         * selector and which are inside of the provided context. Callback will be
         * also invoked on elements which a currently present.
         *
         * @param {String} selector - CSS selector.
         * @param {Function} callback - Function that will invoked when node appears.
         * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.
         */
        get: function (selector, callback, ctx) {
            var data,
                nodes;

            data = {
                ctx: ctx || document.body,
                type: 'add',
                callback: callback,
                invoked: []
            };

            nodes = $(selector, data.ctx).toArray();

            nodes.forEach(function (node) {
                trigger(node, data);
            });

            addSelectorListener(selector, data);
        },

        /**
         * Adds listener for the nodes removal.
         *
         * @param {(jQueryObject|HTMLElement|Array|String)} selector
         * @param {Function} callback - Function that will invoked when node is removed.
         * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.
         */
        remove: function (selector, callback, ctx) {
            var nodes = [],
                data;

            data = {
                ctx: ctx || document.body,
                type: 'remove',
                callback: callback,
                invoked: []
            };

            if (typeof selector === 'object') {
                nodes = !_.isUndefined(selector.length) ?
                    _.toArray(selector) :
                    [selector];
            } else if (_.isString(selector)) {
                nodes = $(selector, ctx).toArray();

                addSelectorListener(selector, data);
            }

            nodes.forEach(function (node) {
                addRemovalListener(node, data);
            });
        },

        /**
         * Removes listeners.
         *
         * @param {String} selector
         * @param {Function} [fn]
         */
        off: function (selector, fn) {
            var selectors = watchers.selectors,
                listeners = selectors[selector];

            if (selector && !fn) {
                delete selectors[selector];
            } else if (listeners && fn) {
                selectors[selector] = listeners.filter(function (data) {
                    return data.callback !== fn;
                });
            }
        }
    };
});

/**
 * 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.
 */

/**
 * @api
 */
define('Magento_Ui/js/lib/view/utils/async',[
    'ko',
    'jquery',
    'underscore',
    'uiRegistry',
    './dom-observer',
    'Magento_Ui/js/lib/knockout/extender/bound-nodes',
    './bindings'
], function (ko, $, _, registry, domObserver, boundedNodes) {
    '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;
    }

    /**
     * Parses provided string and extracts
     * component, context and selector data from it.
     *
     * @param {String} str - String to be processed.
     * @returns {Object} Data retrieved from string.
     *
     * @example Sample format.
     *      '{{component}}:{{ctx}} -> {{selector}}'
     *
     *      component - Name of component.
     *      ctx - Selector of the root node upon which component is binded.
     *      selector - Selector of DOM elements located
     *          inside of a previously specified context.
     */
    function parseSelector(str) {
        var data    = str.trim().split('->'),
            result  = {},
            componentData;

        if (data.length === 1) {
            if (!~data[0].indexOf(':')) {
                result.selector = data[0];
            } else {
                componentData = data[0];
            }
        } else {
            componentData   = data[0];
            result.selector = data[1];
        }

        if (componentData) {
            componentData = componentData.split(':');

            result.component = componentData[0];
            result.ctx = componentData[1];
        }

        _.each(result, function (value, key) {
            result[key] = value.trim();
        });

        return result;
    }

    /**
     * Internal method used to normalize argumnets passed
     * to 'async' module methods.
     *
     * @param {(String|Object)} selector
     * @param {(HTMLElement|Object|String)} [ctx]
     * @returns {Object}
     */
    function parseData(selector, ctx) {
        var data = {};

        if (arguments.length === 2) {
            data.selector = selector;

            if (isDomElement(ctx)) {
                data.ctx = ctx;
            } else {
                data.component = ctx;
                data.ctx = '*';
            }
        } else {
            data = _.isString(selector) ?
                parseSelector(selector) :
                selector;
        }

        return data;
    }

    /**
     * Creates promise that will be resolved
     * when requested component is registred.
     *
     * @param {String} name - Name of component.
     * @returns {jQueryPromise}
     */
    function waitComponent(name) {
        var deffer = $.Deferred();

        if (_.isString(name)) {
            registry.get(name, function (component) {
                deffer.resolve(component);
            });
        } else {
            deffer.resolve(name);
        }

        return deffer.promise();
    }

    /**
     * Creates listener for the nodes binded to provided component.
     *
     * @param {Object} data - Listener data.
     * @param {Object} component - Associated with nodes component.
     */
    function setRootListener(data, component) {
        boundedNodes.get(component, function (root) {
            if (!$(root).is(data.ctx || '*')) {
                return;
            }

            data.selector ?
                domObserver.get(data.selector, data.fn, root) :
                data.fn(root);
        });
    }

    /*eslint-disable no-unused-vars*/
    /**
     * Sets listener for the appearance of elements which
     * matches specified selector data.
     *
     * @param {(String|Object)} selector - Valid css selector or a string
     *      in format acceptable by 'parseSelector' method or an object with
     *      'component', 'selector' and 'ctx' properties.
     * @param {(HTMLElement|Object|String)} [ctx] - Optional context parameter
     *      which might be a DOM element, component instance or components' name.
     * @param {Function} fn - Callback that will be invoked
     *      when required DOM element appears.
     *
     * @example
     *      Creating listener of the 'span' nodes appearance,
     *      located inside of 'div' nodes, which are binded to 'cms_page_listing' component:
     *
     *      $.async('cms_page_listing:div -> span', function (node) {});
     *
     * @example Another syntaxes of the previous example.
     *      $.async({
     *          component: 'cms_page_listing',
     *          ctx: 'div',
     *          selector: 'span'
     *       }, function (node) {});
     *
     * @example Listens for appearance of any child node inside of specified component.
     *      $.async('> *', 'cms_page_lsiting', function (node) {});
     *
     * @example Listens for appearance of 'span' nodes inside of specific context.
     *      $.async('span', document.getElementById('test'), function (node) {});
     */
    $.async = function (selector, ctx, fn) {
        var args = _.toArray(arguments),
            data = parseData.apply(null, _.initial(args));

        data.fn = _.last(args);

        if (data.component) {
            waitComponent(data.component)
                .then(setRootListener.bind(null, data));
        } else {
            domObserver.get(data.selector, data.fn, data.ctx);
        }
    };

    /*eslint-enable no-unused-vars*/

    _.extend($.async, {

        /*eslint-disable no-unused-vars*/
        /**
         * Returns collection of elements found by provided selector data.
         *
         * @param {(String|Object)} selector - See 'async' definition.
         * @param {(HTMLElement|Object|String)} [ctx] - See 'async' definition.
         * @returns {Array} An array of DOM elements.
         */
        get: function (selector, ctx) {
            var data        = parseData.apply(null, arguments),
                component   = data.component,
                nodes;

            if (!component) {
                return $(data.selector, data.ctx).toArray();
            } else if (_.isString(component)) {
                component = registry.get(component);
            }

            if (!component) {
                return [];
            }

            nodes = boundedNodes.get(component);
            nodes = $(nodes).filter(data.ctx).toArray();

            return data.selector ?
                $(data.selector, nodes).toArray() :
                nodes;
        },

        /*eslint-enable no-unused-vars*/

        /**
         * Sets removal listener of the specified nodes.
         *
         * @param {(HTMLElement|Array|ArrayLike)} nodes - Nodes whose removal to track.
         * @param {Function} fn - Callback that will be invoked when node is removed.
         */
        remove: function (nodes, fn) {
            domObserver.remove(nodes, fn);
        },

        parseSelector: parseSelector
    });

    return $;
});

/**
 * 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/knockout/bindings/resizable',[
    'ko',
    'jquery',
    'Magento_Ui/js/lib/view/utils/async',
    'uiRegistry',
    'underscore',
    '../template/renderer'
], function (ko, $, async, registry, _, renderer) {
    'use strict';

    var sizeOptions = [
            'minHeight',
            'maxHeight',
            'minWidth',
            'maxWidth'
        ],

        handles = {
            height: '.ui-resizable-s, .ui-resizable-n',
            width: '.ui-resizable-w, .ui-resizable-e'
        };

    /**
     * Recalcs visibility of handles, width and height of resizable based on content
     * @param {HTMLElement} element
     */
    function adjustSize(element) {
        var maxHeight,
            maxWidth;

        element = $(element);
        maxHeight = element.resizable('option').maxHeight;
        maxWidth = element.resizable('option').maxWidth;

        if (maxHeight && element.height() > maxHeight) {
            element.height(maxHeight + 1);
            $(handles.height).hide();
        } else {
            $(handles.height).show();
        }

        if (maxWidth && element.width() > maxWidth) {
            element.width(maxWidth + 1);
            $(handles.width).hide();
        } else {
            $(handles.width).show();
        }
    }

    /**
     * Recalcs allowed min, max width and height based on configured selectors
     * @param {Object} sizeConstraints
     * @param {String} componentName
     * @param {HTMLElement} element
     * @param {Boolean} hasWidthUpdate
     */
    function recalcAllowedSize(sizeConstraints, componentName, element, hasWidthUpdate) {
        var size;

        element = $(element);

        if (!element.data('resizable')) {
            return;
        }

        if (!hasWidthUpdate) {
            element.css('width', 'auto');
        }

        _.each(sizeConstraints, function (selector, key) {
            async.async({
                component: componentName,
                selector: selector
            }, function (elem) {
                size = key.indexOf('Height') !== -1 ? $(elem).outerHeight(true) : $(elem).outerWidth(true);

                if (element.data('resizable')) {
                    element.resizable('option', key, size + 1);
                }
            });
        }, this);

        adjustSize(element);
    }

    /**
     * Preprocess config to separate options,
     * which must be processed further before applying
     *
     * @param {Object} config
     * @param {Object} viewModel
     * @param {*} element
     * @return {Object} config
     */
    function processConfig(config, viewModel, element) {
        var sizeConstraint,
            sizeConstraints = {},
            recalc,
            hasWidthUpdate;

        if (_.isEmpty(config)) {
            return {};
        }
        _.each(sizeOptions, function (key) {
            sizeConstraint = config[key];

            if (sizeConstraint && !_.isNumber(sizeConstraint)) {
                sizeConstraints[key] = sizeConstraint;
                delete config[key];
            }
        });
        hasWidthUpdate =  _.some(sizeConstraints, function (value, key) {
            return key.indexOf('Width') !== -1;
        });

        recalc = recalcAllowedSize.bind(null, sizeConstraints, viewModel.name, element, hasWidthUpdate);
        config.start = recalc;
        $(window).on('resize.resizable', recalc);
        registry.get(viewModel.provider).on('reloaded', recalc);

        return config;
    }

    ko.bindingHandlers.resizable = {

        /**
         * Binding init callback.
         *
         * @param {*} element
         * @param {Function} valueAccessor
         * @param {Function} allBindings
         * @param {Object} viewModel
         */
        init: function (element, valueAccessor, allBindings, viewModel) {
            var config = processConfig(valueAccessor(), viewModel, element);

            require(['jquery-ui-modules/resizable'], function () {
                if ($.fn.resizable) {
                    $(element).resizable(config);
                }
            });
        }
    };

    renderer.addAttribute('resizable');
});

/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 *
 * Open source under the BSD License.
 *
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 *
 * Neither the name of the author nor the names of contributors may be used to endorse
 * or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/
(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module depending on jQuery.
		define('WeltPixel_DesignElements/js/canvas/jquery.important',["jquery"], factory);
	} else {
		// No AMD. Register plugin with global jQuery object.
		factory(jQuery);
	}
}(function ($) {

// t: current time, b: begInnIng value, c: change In value, d: duration
	jQuery.easing['jswing'] = jQuery.easing['swing'];

	jQuery.extend( jQuery.easing,
		{
			def: 'easeOutQuad',
			swing: function (x, t, b, c, d) {
				//alert(jQuery.easing.default);
				return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
			},
			easeInQuad: function (x, t, b, c, d) {
				return c*(t/=d)*t + b;
			},
			easeOutQuad: function (x, t, b, c, d) {
				return -c *(t/=d)*(t-2) + b;
			},
			easeInOutQuad: function (x, t, b, c, d) {
				if ((t/=d/2) < 1) return c/2*t*t + b;
				return -c/2 * ((--t)*(t-2) - 1) + b;
			},
			easeInCubic: function (x, t, b, c, d) {
				return c*(t/=d)*t*t + b;
			},
			easeOutCubic: function (x, t, b, c, d) {
				return c*((t=t/d-1)*t*t + 1) + b;
			},
			easeInOutCubic: function (x, t, b, c, d) {
				if ((t/=d/2) < 1) return c/2*t*t*t + b;
				return c/2*((t-=2)*t*t + 2) + b;
			},
			easeInQuart: function (x, t, b, c, d) {
				return c*(t/=d)*t*t*t + b;
			},
			easeOutQuart: function (x, t, b, c, d) {
				return -c * ((t=t/d-1)*t*t*t - 1) + b;
			},
			easeInOutQuart: function (x, t, b, c, d) {
				if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
				return -c/2 * ((t-=2)*t*t*t - 2) + b;
			},
			easeInQuint: function (x, t, b, c, d) {
				return c*(t/=d)*t*t*t*t + b;
			},
			easeOutQuint: function (x, t, b, c, d) {
				return c*((t=t/d-1)*t*t*t*t + 1) + b;
			},
			easeInOutQuint: function (x, t, b, c, d) {
				if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
				return c/2*((t-=2)*t*t*t*t + 2) + b;
			},
			easeInSine: function (x, t, b, c, d) {
				return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
			},
			easeOutSine: function (x, t, b, c, d) {
				return c * Math.sin(t/d * (Math.PI/2)) + b;
			},
			easeInOutSine: function (x, t, b, c, d) {
				return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
			},
			easeInExpo: function (x, t, b, c, d) {
				return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
			},
			easeOutExpo: function (x, t, b, c, d) {
				return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
			},
			easeInOutExpo: function (x, t, b, c, d) {
				if (t==0) return b;
				if (t==d) return b+c;
				if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
				return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
			},
			easeInCirc: function (x, t, b, c, d) {
				return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
			},
			easeOutCirc: function (x, t, b, c, d) {
				return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
			},
			easeInOutCirc: function (x, t, b, c, d) {
				if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
				return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
			},
			easeInElastic: function (x, t, b, c, d) {
				var s=1.70158;var p=0;var a=c;
				if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
				if (a < Math.abs(c)) { a=c; var s=p/4; }
				else var s = p/(2*Math.PI) * Math.asin (c/a);
				return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
			},
			easeOutElastic: function (x, t, b, c, d) {
				var s=1.70158;var p=0;var a=c;
				if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
				if (a < Math.abs(c)) { a=c; var s=p/4; }
				else var s = p/(2*Math.PI) * Math.asin (c/a);
				return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
			},
			easeInOutElastic: function (x, t, b, c, d) {
				var s=1.70158;var p=0;var a=c;
				if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
				if (a < Math.abs(c)) { a=c; var s=p/4; }
				else var s = p/(2*Math.PI) * Math.asin (c/a);
				if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
				return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
			},
			easeInBack: function (x, t, b, c, d, s) {
				if (s == undefined) s = 1.70158;
				return c*(t/=d)*t*((s+1)*t - s) + b;
			},
			easeOutBack: function (x, t, b, c, d, s) {
				if (s == undefined) s = 1.70158;
				return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
			},
			easeInOutBack: function (x, t, b, c, d, s) {
				if (s == undefined) s = 1.70158;
				if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
				return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
			},
			easeInBounce: function (x, t, b, c, d) {
				return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
			},
			easeOutBounce: function (x, t, b, c, d) {
				if ((t/=d) < (1/2.75)) {
					return c*(7.5625*t*t) + b;
				} else if (t < (2/2.75)) {
					return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
				} else if (t < (2.5/2.75)) {
					return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
				} else {
					return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
				}
			},
			easeInOutBounce: function (x, t, b, c, d) {
				if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
				return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
			}
		});

	/*
 * jQuery.appear
 * https://github.com/bas2k/jquery.appear/
 * http://code.google.com/p/jquery-appear/
 * http://bas2k.ru/
 *
 * Copyright (c) 2009 Michael Hixson
 * Copyright (c) 2012-2014 Alexander Brovikov
 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
 */
	$.fn.appear = function(fn, options) {

		var settings = $.extend({

			//arbitrary data to pass to fn
			data: undefined,

			//call fn only on the first appear?
			one: true,

			// X & Y accuracy
			accX: 0,
			accY: 0

		}, options);

		return this.each(function() {

			var t = $(this);

			//whether the element is currently visible
			t.appeared = false;

			if (!fn) {

				//trigger the custom event
				t.trigger('appear', settings.data);
				return;
			}

			var w = $(window);

			//fires the appear event when appropriate
			var check = function() {

				//is the element hidden?
				if (!t.is(':visible')) {

					//it became hidden
					t.appeared = false;
					return;
				}

				//is the element inside the visible window?
				var a = w.scrollLeft();
				var b = w.scrollTop();
				var o = t.offset();
				var x = o.left;
				var y = o.top;

				var ax = settings.accX;
				var ay = settings.accY;
				var th = t.height();
				var wh = w.height();
				var tw = t.width();
				var ww = w.width();

				if (y + th + ay >= b &&
					y <= b + wh + ay &&
					x + tw + ax >= a &&
					x <= a + ww + ax) {

					//trigger the custom event
					if (!t.appeared) t.trigger('appear', settings.data);

				} else {

					//it scrolled out of view
					t.appeared = false;
				}
			};

			//create a modified fn with some additional logic
			var modifiedFn = function() {

				//mark the element as visible
				t.appeared = true;

				//is this supposed to happen only once?
				if (settings.one) {

					//remove the check
					w.unbind('scroll', check);
					var i = $.inArray(check, $.fn.appear.checks);
					if (i >= 0) $.fn.appear.checks.splice(i, 1);
				}

				//trigger the original fn
				fn.apply(this, arguments);
			};

			//bind the modified fn to the element
			if (settings.one) t.one('appear', settings.data, modifiedFn);
			else t.bind('appear', settings.data, modifiedFn);

			//check whenever the window scrolls
			w.scroll(check);

			//check whenever the dom changes
			$.fn.appear.checks.push(check);

			//check now
			(check)();
		});
	};

	//keep a queue of appearance checks
	$.extend($.fn.appear, {

		checks: [],
		timeout: null,

		//process the queue
		checkAll: function() {
			var length = $.fn.appear.checks.length;
			if (length > 0) while (length--) ($.fn.appear.checks[length])();
		},

		//check the queue asynchronously
		run: function() {
			if ($.fn.appear.timeout) clearTimeout($.fn.appear.timeout);
			$.fn.appear.timeout = setTimeout($.fn.appear.checkAll, 20);
		}
	});

	//run checks when these methods are called
	$.each(['append', 'prepend', 'after', 'before', 'attr',
		'removeAttr', 'addClass', 'removeClass', 'toggleClass',
		'remove', 'css', 'show', 'hide'], function(i, n) {
		var old = $.fn[n];
		if (old) {
			$.fn[n] = function() {
				var r = old.apply(this, arguments);
				$.fn.appear.run();
				return r;
			}
		}
	});
}));

/**
 * 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/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;
});

//! moment.js
//! version : 2.29.4
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define('moment',t):e.moment=t()}(this,function(){"use strict";var H;function f(){return H.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function F(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function c(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function L(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(var t in e)if(c(e,t))return;return 1}function o(e){return void 0===e}function u(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function V(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function G(e,t){for(var n=[],s=e.length,i=0;i<s;++i)n.push(t(e[i],i));return n}function E(e,t){for(var n in t)c(t,n)&&(e[n]=t[n]);return c(t,"toString")&&(e.toString=t.toString),c(t,"valueOf")&&(e.valueOf=t.valueOf),e}function l(e,t,n,s){return Pt(e,t,n,s,!0).utc()}function m(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidEra:null,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],era:null,meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function A(e){if(null==e._isValid){var t=m(e),n=j.call(t.parsedDateParts,function(e){return null!=e}),n=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidEra&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(n=n&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return n;e._isValid=n}return e._isValid}function I(e){var t=l(NaN);return null!=e?E(m(t),e):m(t).userInvalidated=!0,t}var j=Array.prototype.some||function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1},Z=f.momentProperties=[],z=!1;function $(e,t){var n,s,i,r=Z.length;if(o(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),o(t._i)||(e._i=t._i),o(t._f)||(e._f=t._f),o(t._l)||(e._l=t._l),o(t._strict)||(e._strict=t._strict),o(t._tzm)||(e._tzm=t._tzm),o(t._isUTC)||(e._isUTC=t._isUTC),o(t._offset)||(e._offset=t._offset),o(t._pf)||(e._pf=m(t)),o(t._locale)||(e._locale=t._locale),0<r)for(n=0;n<r;n++)o(i=t[s=Z[n]])||(e[s]=i);return e}function q(e){$(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===z&&(z=!0,f.updateOffset(this),z=!1)}function h(e){return e instanceof q||null!=e&&null!=e._isAMomentObject}function B(e){!1===f.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function e(r,a){var o=!0;return E(function(){if(null!=f.deprecationHandler&&f.deprecationHandler(null,r),o){for(var e,t,n=[],s=arguments.length,i=0;i<s;i++){if(e="","object"==typeof arguments[i]){for(t in e+="\n["+i+"] ",arguments[0])c(arguments[0],t)&&(e+=t+": "+arguments[0][t]+", ");e=e.slice(0,-2)}else e=arguments[i];n.push(e)}B(r+"\nArguments: "+Array.prototype.slice.call(n).join("")+"\n"+(new Error).stack),o=!1}return a.apply(this,arguments)},a)}var J={};function Q(e,t){null!=f.deprecationHandler&&f.deprecationHandler(e,t),J[e]||(B(t),J[e]=!0)}function d(e){return"undefined"!=typeof Function&&e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function X(e,t){var n,s=E({},e);for(n in t)c(t,n)&&(F(e[n])&&F(t[n])?(s[n]={},E(s[n],e[n]),E(s[n],t[n])):null!=t[n]?s[n]=t[n]:delete s[n]);for(n in e)c(e,n)&&!c(t,n)&&F(e[n])&&(s[n]=E({},s[n]));return s}function K(e){null!=e&&this.set(e)}f.suppressDeprecationWarnings=!1,f.deprecationHandler=null;var ee=Object.keys||function(e){var t,n=[];for(t in e)c(e,t)&&n.push(t);return n};function r(e,t,n){var s=""+Math.abs(e);return(0<=e?n?"+":"":"-")+Math.pow(10,Math.max(0,t-s.length)).toString().substr(1)+s}var te=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,ne=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,se={},ie={};function s(e,t,n,s){var i="string"==typeof s?function(){return this[s]()}:s;e&&(ie[e]=i),t&&(ie[t[0]]=function(){return r(i.apply(this,arguments),t[1],t[2])}),n&&(ie[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function re(e,t){return e.isValid()?(t=ae(t,e.localeData()),se[t]=se[t]||function(s){for(var e,i=s.match(te),t=0,r=i.length;t<r;t++)ie[i[t]]?i[t]=ie[i[t]]:i[t]=(e=i[t]).match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"");return function(e){for(var t="",n=0;n<r;n++)t+=d(i[n])?i[n].call(e,s):i[n];return t}}(t),se[t](e)):e.localeData().invalidDate()}function ae(e,t){var n=5;function s(e){return t.longDateFormat(e)||e}for(ne.lastIndex=0;0<=n&&ne.test(e);)e=e.replace(ne,s),ne.lastIndex=0,--n;return e}var oe={};function t(e,t){var n=e.toLowerCase();oe[n]=oe[n+"s"]=oe[t]=e}function _(e){return"string"==typeof e?oe[e]||oe[e.toLowerCase()]:void 0}function ue(e){var t,n,s={};for(n in e)c(e,n)&&(t=_(n))&&(s[t]=e[n]);return s}var le={};function n(e,t){le[e]=t}function he(e){return e%4==0&&e%100!=0||e%400==0}function y(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function g(e){var e=+e,t=0;return t=0!=e&&isFinite(e)?y(e):t}function de(t,n){return function(e){return null!=e?(fe(this,t,e),f.updateOffset(this,n),this):ce(this,t)}}function ce(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function fe(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&he(e.year())&&1===e.month()&&29===e.date()?(n=g(n),e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),We(n,e.month()))):e._d["set"+(e._isUTC?"UTC":"")+t](n))}var i=/\d/,w=/\d\d/,me=/\d{3}/,_e=/\d{4}/,ye=/[+-]?\d{6}/,p=/\d\d?/,ge=/\d\d\d\d?/,we=/\d\d\d\d\d\d?/,pe=/\d{1,3}/,ke=/\d{1,4}/,ve=/[+-]?\d{1,6}/,Me=/\d+/,De=/[+-]?\d+/,Se=/Z|[+-]\d\d:?\d\d/gi,Ye=/Z|[+-]\d\d(?::?\d\d)?/gi,k=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;function v(e,n,s){be[e]=d(n)?n:function(e,t){return e&&s?s:n}}function Oe(e,t){return c(be,e)?be[e](t._strict,t._locale):new RegExp(M(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function M(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var be={},xe={};function D(e,n){var t,s,i=n;for("string"==typeof e&&(e=[e]),u(n)&&(i=function(e,t){t[n]=g(e)}),s=e.length,t=0;t<s;t++)xe[e[t]]=i}function Te(e,i){D(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}var S,Y=0,O=1,b=2,x=3,T=4,N=5,Ne=6,Pe=7,Re=8;function We(e,t){if(isNaN(e)||isNaN(t))return NaN;var n=(t%(n=12)+n)%n;return e+=(t-n)/12,1==n?he(e)?29:28:31-n%7%2}S=Array.prototype.indexOf||function(e){for(var t=0;t<this.length;++t)if(this[t]===e)return t;return-1},s("M",["MM",2],"Mo",function(){return this.month()+1}),s("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),s("MMMM",0,0,function(e){return this.localeData().months(this,e)}),t("month","M"),n("month",8),v("M",p),v("MM",p,w),v("MMM",function(e,t){return t.monthsShortRegex(e)}),v("MMMM",function(e,t){return t.monthsRegex(e)}),D(["M","MM"],function(e,t){t[O]=g(e)-1}),D(["MMM","MMMM"],function(e,t,n,s){s=n._locale.monthsParse(e,s,n._strict);null!=s?t[O]=s:m(n).invalidMonth=e});var Ce="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Ue="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),He=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Fe=k,Le=k;function Ve(e,t){var n;if(e.isValid()){if("string"==typeof t)if(/^\d+$/.test(t))t=g(t);else if(!u(t=e.localeData().monthsParse(t)))return;n=Math.min(e.date(),We(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n)}}function Ge(e){return null!=e?(Ve(this,e),f.updateOffset(this,!0),this):ce(this,"Month")}function Ee(){function e(e,t){return t.length-e.length}for(var t,n=[],s=[],i=[],r=0;r<12;r++)t=l([2e3,r]),n.push(this.monthsShort(t,"")),s.push(this.months(t,"")),i.push(this.months(t,"")),i.push(this.monthsShort(t,""));for(n.sort(e),s.sort(e),i.sort(e),r=0;r<12;r++)n[r]=M(n[r]),s[r]=M(s[r]);for(r=0;r<24;r++)i[r]=M(i[r]);this._monthsRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+n.join("|")+")","i")}function Ae(e){return he(e)?366:365}s("Y",0,0,function(){var e=this.year();return e<=9999?r(e,4):"+"+e}),s(0,["YY",2],0,function(){return this.year()%100}),s(0,["YYYY",4],0,"year"),s(0,["YYYYY",5],0,"year"),s(0,["YYYYYY",6,!0],0,"year"),t("year","y"),n("year",1),v("Y",De),v("YY",p,w),v("YYYY",ke,_e),v("YYYYY",ve,ye),v("YYYYYY",ve,ye),D(["YYYYY","YYYYYY"],Y),D("YYYY",function(e,t){t[Y]=2===e.length?f.parseTwoDigitYear(e):g(e)}),D("YY",function(e,t){t[Y]=f.parseTwoDigitYear(e)}),D("Y",function(e,t){t[Y]=parseInt(e,10)}),f.parseTwoDigitYear=function(e){return g(e)+(68<g(e)?1900:2e3)};var Ie=de("FullYear",!0);function je(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}function Ze(e){var t;return e<100&&0<=e?((t=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,t)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function ze(e,t,n){n=7+t-n;return n-(7+Ze(e,0,n).getUTCDay()-t)%7-1}function $e(e,t,n,s,i){var r,t=1+7*(t-1)+(7+n-s)%7+ze(e,s,i),n=t<=0?Ae(r=e-1)+t:t>Ae(e)?(r=e+1,t-Ae(e)):(r=e,t);return{year:r,dayOfYear:n}}function qe(e,t,n){var s,i,r=ze(e.year(),t,n),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+P(i=e.year()-1,t,n):r>P(e.year(),t,n)?(s=r-P(e.year(),t,n),i=e.year()+1):(i=e.year(),s=r),{week:s,year:i}}function P(e,t,n){var s=ze(e,t,n),t=ze(e+1,t,n);return(Ae(e)-s+t)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),t("week","w"),t("isoWeek","W"),n("week",5),n("isoWeek",5),v("w",p),v("ww",p,w),v("W",p),v("WW",p,w),Te(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=g(e)});function Be(e,t){return e.slice(t,7).concat(e.slice(0,t))}s("d",0,"do","day"),s("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),s("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),s("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),s("e",0,0,"weekday"),s("E",0,0,"isoWeekday"),t("day","d"),t("weekday","e"),t("isoWeekday","E"),n("day",11),n("weekday",11),n("isoWeekday",11),v("d",p),v("e",p),v("E",p),v("dd",function(e,t){return t.weekdaysMinRegex(e)}),v("ddd",function(e,t){return t.weekdaysShortRegex(e)}),v("dddd",function(e,t){return t.weekdaysRegex(e)}),Te(["dd","ddd","dddd"],function(e,t,n,s){s=n._locale.weekdaysParse(e,s,n._strict);null!=s?t.d=s:m(n).invalidWeekday=e}),Te(["d","e","E"],function(e,t,n,s){t[s]=g(e)});var Je="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Qe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Xe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Ke=k,et=k,tt=k;function nt(){function e(e,t){return t.length-e.length}for(var t,n,s,i=[],r=[],a=[],o=[],u=0;u<7;u++)s=l([2e3,1]).day(u),t=M(this.weekdaysMin(s,"")),n=M(this.weekdaysShort(s,"")),s=M(this.weekdays(s,"")),i.push(t),r.push(n),a.push(s),o.push(t),o.push(n),o.push(s);i.sort(e),r.sort(e),a.sort(e),o.sort(e),this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+r.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+i.join("|")+")","i")}function st(){return this.hours()%12||12}function it(e,t){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function rt(e,t){return t._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,st),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)}),s("hmmss",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)+r(this.seconds(),2)}),s("Hmm",0,0,function(){return""+this.hours()+r(this.minutes(),2)}),s("Hmmss",0,0,function(){return""+this.hours()+r(this.minutes(),2)+r(this.seconds(),2)}),it("a",!0),it("A",!1),t("hour","h"),n("hour",13),v("a",rt),v("A",rt),v("H",p),v("h",p),v("k",p),v("HH",p,w),v("hh",p,w),v("kk",p,w),v("hmm",ge),v("hmmss",we),v("Hmm",ge),v("Hmmss",we),D(["H","HH"],x),D(["k","kk"],function(e,t,n){e=g(e);t[x]=24===e?0:e}),D(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),D(["h","hh"],function(e,t,n){t[x]=g(e),m(n).bigHour=!0}),D("hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s)),m(n).bigHour=!0}),D("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i)),m(n).bigHour=!0}),D("Hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s))}),D("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i))});k=de("Hours",!0);var at,ot={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:Ue,week:{dow:0,doy:6},weekdays:Je,weekdaysMin:Xe,weekdaysShort:Qe,meridiemParse:/[ap]\.?m?\.?/i},R={},ut={};function lt(e){return e&&e.toLowerCase().replace("_","-")}function ht(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=lt(e[r]).split("-")).length,n=(n=lt(e[r+1]))?n.split("-"):null;0<t;){if(s=dt(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&function(e,t){for(var n=Math.min(e.length,t.length),s=0;s<n;s+=1)if(e[s]!==t[s])return s;return n}(i,n)>=t-1)break;t--}r++}return at}function dt(t){var e;if(void 0===R[t]&&"undefined"!=typeof module&&module&&module.exports&&null!=t.match("^[^/\\\\]*$"))try{e=at._abbr,require("./locale/"+t),ct(e)}catch(e){R[t]=null}return R[t]}function ct(e,t){return e&&((t=o(t)?mt(e):ft(e,t))?at=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),at._abbr}function ft(e,t){if(null===t)return delete R[e],null;var n,s=ot;if(t.abbr=e,null!=R[e])Q("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=R[e]._config;else if(null!=t.parentLocale)if(null!=R[t.parentLocale])s=R[t.parentLocale]._config;else{if(null==(n=dt(t.parentLocale)))return ut[t.parentLocale]||(ut[t.parentLocale]=[]),ut[t.parentLocale].push({name:e,config:t}),null;s=n._config}return R[e]=new K(X(s,t)),ut[e]&&ut[e].forEach(function(e){ft(e.name,e.config)}),ct(e),R[e]}function mt(e){var t;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return at;if(!a(e)){if(t=dt(e))return t;e=[e]}return ht(e)}function _t(e){var t=e._a;return t&&-2===m(e).overflow&&(t=t[O]<0||11<t[O]?O:t[b]<1||t[b]>We(t[Y],t[O])?b:t[x]<0||24<t[x]||24===t[x]&&(0!==t[T]||0!==t[N]||0!==t[Ne])?x:t[T]<0||59<t[T]?T:t[N]<0||59<t[N]?N:t[Ne]<0||999<t[Ne]?Ne:-1,m(e)._overflowDayOfYear&&(t<Y||b<t)&&(t=b),m(e)._overflowWeeks&&-1===t&&(t=Pe),m(e)._overflowWeekday&&-1===t&&(t=Re),m(e).overflow=t),e}var yt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,gt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,wt=/Z|[+-]\d\d(?::?\d\d)?/,pt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],kt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],vt=/^\/?Date\((-?\d+)/i,Mt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,Dt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function St(e){var t,n,s,i,r,a,o=e._i,u=yt.exec(o)||gt.exec(o),o=pt.length,l=kt.length;if(u){for(m(e).iso=!0,t=0,n=o;t<n;t++)if(pt[t][1].exec(u[1])){i=pt[t][0],s=!1!==pt[t][2];break}if(null==i)e._isValid=!1;else{if(u[3]){for(t=0,n=l;t<n;t++)if(kt[t][1].exec(u[3])){r=(u[2]||" ")+kt[t][0];break}if(null==r)return void(e._isValid=!1)}if(s||null==r){if(u[4]){if(!wt.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),Tt(e)}else e._isValid=!1}}else e._isValid=!1}function Yt(e,t,n,s,i,r){e=[function(e){e=parseInt(e,10);{if(e<=49)return 2e3+e;if(e<=999)return 1900+e}return e}(e),Ue.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&e.push(parseInt(r,10)),e}function Ot(e){var t,n,s,i,r=Mt.exec(e._i.replace(/\([^()]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));r?(t=Yt(r[4],r[3],r[2],r[5],r[6],r[7]),n=r[1],s=t,i=e,n&&Qe.indexOf(n)!==new Date(s[0],s[1],s[2]).getDay()?(m(i).weekdayMismatch=!0,i._isValid=!1):(e._a=t,e._tzm=(n=r[8],s=r[9],i=r[10],n?Dt[n]:s?0:60*(((n=parseInt(i,10))-(s=n%100))/100)+s),e._d=Ze.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),m(e).rfc2822=!0)):e._isValid=!1}function bt(e,t,n){return null!=e?e:null!=t?t:n}function xt(e){var t,n,s,i,r,a,o,u,l,h,d,c=[];if(!e._d){for(s=e,i=new Date(f.now()),n=s._useUTC?[i.getUTCFullYear(),i.getUTCMonth(),i.getUTCDate()]:[i.getFullYear(),i.getMonth(),i.getDate()],e._w&&null==e._a[b]&&null==e._a[O]&&(null!=(i=(s=e)._w).GG||null!=i.W||null!=i.E?(u=1,l=4,r=bt(i.GG,s._a[Y],qe(W(),1,4).year),a=bt(i.W,1),((o=bt(i.E,1))<1||7<o)&&(h=!0)):(u=s._locale._week.dow,l=s._locale._week.doy,d=qe(W(),u,l),r=bt(i.gg,s._a[Y],d.year),a=bt(i.w,d.week),null!=i.d?((o=i.d)<0||6<o)&&(h=!0):null!=i.e?(o=i.e+u,(i.e<0||6<i.e)&&(h=!0)):o=u),a<1||a>P(r,u,l)?m(s)._overflowWeeks=!0:null!=h?m(s)._overflowWeekday=!0:(d=$e(r,a,o,u,l),s._a[Y]=d.year,s._dayOfYear=d.dayOfYear)),null!=e._dayOfYear&&(i=bt(e._a[Y],n[Y]),(e._dayOfYear>Ae(i)||0===e._dayOfYear)&&(m(e)._overflowDayOfYear=!0),h=Ze(i,0,e._dayOfYear),e._a[O]=h.getUTCMonth(),e._a[b]=h.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=c[t]=n[t];for(;t<7;t++)e._a[t]=c[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[x]&&0===e._a[T]&&0===e._a[N]&&0===e._a[Ne]&&(e._nextDay=!0,e._a[x]=0),e._d=(e._useUTC?Ze:je).apply(null,c),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[x]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(m(e).weekdayMismatch=!0)}}function Tt(e){if(e._f===f.ISO_8601)St(e);else if(e._f===f.RFC_2822)Ot(e);else{e._a=[],m(e).empty=!0;for(var t,n,s,i,r,a=""+e._i,o=a.length,u=0,l=ae(e._f,e._locale).match(te)||[],h=l.length,d=0;d<h;d++)n=l[d],(t=(a.match(Oe(n,e))||[])[0])&&(0<(s=a.substr(0,a.indexOf(t))).length&&m(e).unusedInput.push(s),a=a.slice(a.indexOf(t)+t.length),u+=t.length),ie[n]?(t?m(e).empty=!1:m(e).unusedTokens.push(n),s=n,r=e,null!=(i=t)&&c(xe,s)&&xe[s](i,r._a,r,s)):e._strict&&!t&&m(e).unusedTokens.push(n);m(e).charsLeftOver=o-u,0<a.length&&m(e).unusedInput.push(a),e._a[x]<=12&&!0===m(e).bigHour&&0<e._a[x]&&(m(e).bigHour=void 0),m(e).parsedDateParts=e._a.slice(0),m(e).meridiem=e._meridiem,e._a[x]=function(e,t,n){if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((e=e.isPM(n))&&t<12&&(t+=12),t=e||12!==t?t:0):t}(e._locale,e._a[x],e._meridiem),null!==(o=m(e).era)&&(e._a[Y]=e._locale.erasConvertYear(o,e._a[Y])),xt(e),_t(e)}}function Nt(e){var t,n,s,i=e._i,r=e._f;if(e._locale=e._locale||mt(e._l),null===i||void 0===r&&""===i)return I({nullInput:!0});if("string"==typeof i&&(e._i=i=e._locale.preparse(i)),h(i))return new q(_t(i));if(V(i))e._d=i;else if(a(r))!function(e){var t,n,s,i,r,a,o=!1,u=e._f.length;if(0===u)return m(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<u;i++)r=0,a=!1,t=$({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],Tt(t),A(t)&&(a=!0),r=(r+=m(t).charsLeftOver)+10*m(t).unusedTokens.length,m(t).score=r,o?r<s&&(s=r,n=t):(null==s||r<s||a)&&(s=r,n=t,a&&(o=!0));E(e,n||t)}(e);else if(r)Tt(e);else if(o(r=(i=e)._i))i._d=new Date(f.now());else V(r)?i._d=new Date(r.valueOf()):"string"==typeof r?(n=i,null!==(t=vt.exec(n._i))?n._d=new Date(+t[1]):(St(n),!1===n._isValid&&(delete n._isValid,Ot(n),!1===n._isValid&&(delete n._isValid,n._strict?n._isValid=!1:f.createFromInputFallback(n))))):a(r)?(i._a=G(r.slice(0),function(e){return parseInt(e,10)}),xt(i)):F(r)?(t=i)._d||(s=void 0===(n=ue(t._i)).day?n.date:n.day,t._a=G([n.year,n.month,s,n.hour,n.minute,n.second,n.millisecond],function(e){return e&&parseInt(e,10)}),xt(t)):u(r)?i._d=new Date(r):f.createFromInputFallback(i);return A(e)||(e._d=null),e}function Pt(e,t,n,s,i){var r={};return!0!==t&&!1!==t||(s=t,t=void 0),!0!==n&&!1!==n||(s=n,n=void 0),(F(e)&&L(e)||a(e)&&0===e.length)&&(e=void 0),r._isAMomentObject=!0,r._useUTC=r._isUTC=i,r._l=n,r._i=e,r._f=t,r._strict=s,(i=new q(_t(Nt(i=r))))._nextDay&&(i.add(1,"d"),i._nextDay=void 0),i}function W(e,t,n,s){return Pt(e,t,n,s,!1)}f.createFromInputFallback=e("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),f.ISO_8601=function(){},f.RFC_2822=function(){};ge=e("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=W.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:I()}),we=e("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=W.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:I()});function Rt(e,t){var n,s;if(!(t=1===t.length&&a(t[0])?t[0]:t).length)return W();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Wt=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ct(e){var e=ue(e),t=e.year||0,n=e.quarter||0,s=e.month||0,i=e.week||e.isoWeek||0,r=e.day||0,a=e.hour||0,o=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(e){var t,n,s=!1,i=Wt.length;for(t in e)if(c(e,t)&&(-1===S.call(Wt,t)||null!=e[t]&&isNaN(e[t])))return!1;for(n=0;n<i;++n)if(e[Wt[n]]){if(s)return!1;parseFloat(e[Wt[n]])!==g(e[Wt[n]])&&(s=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*o+1e3*a*60*60,this._days=+r+7*i,this._months=+s+3*n+12*t,this._data={},this._locale=mt(),this._bubble()}function Ut(e){return e instanceof Ct}function Ht(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Ft(e,n){s(e,0,0,function(){var e=this.utcOffset(),t="+";return e<0&&(e=-e,t="-"),t+r(~~(e/60),2)+n+r(~~e%60,2)})}Ft("Z",":"),Ft("ZZ",""),v("Z",Ye),v("ZZ",Ye),D(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Vt(Ye,e)});var Lt=/([\+\-]|\d\d)/gi;function Vt(e,t){var t=(t||"").match(e);return null===t?null:0===(t=60*(e=((t[t.length-1]||[])+"").match(Lt)||["-",0,0])[1]+g(e[2]))?0:"+"===e[0]?t:-t}function Gt(e,t){var n;return t._isUTC?(t=t.clone(),n=(h(e)||V(e)?e:W(e)).valueOf()-t.valueOf(),t._d.setTime(t._d.valueOf()+n),f.updateOffset(t,!1),t):W(e).local()}function Et(e){return-Math.round(e._d.getTimezoneOffset())}function At(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}f.updateOffset=function(){};var It=/^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,jt=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function C(e,t){var n,s=e,i=null;return Ut(e)?s={ms:e._milliseconds,d:e._days,M:e._months}:u(e)||!isNaN(+e)?(s={},t?s[t]=+e:s.milliseconds=+e):(i=It.exec(e))?(n="-"===i[1]?-1:1,s={y:0,d:g(i[b])*n,h:g(i[x])*n,m:g(i[T])*n,s:g(i[N])*n,ms:g(Ht(1e3*i[Ne]))*n}):(i=jt.exec(e))?(n="-"===i[1]?-1:1,s={y:Zt(i[2],n),M:Zt(i[3],n),w:Zt(i[4],n),d:Zt(i[5],n),h:Zt(i[6],n),m:Zt(i[7],n),s:Zt(i[8],n)}):null==s?s={}:"object"==typeof s&&("from"in s||"to"in s)&&(t=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=Gt(t,e),e.isBefore(t)?n=zt(e,t):((n=zt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(W(s.from),W(s.to)),(s={}).ms=t.milliseconds,s.M=t.months),i=new Ct(s),Ut(e)&&c(e,"_locale")&&(i._locale=e._locale),Ut(e)&&c(e,"_isValid")&&(i._isValid=e._isValid),i}function Zt(e,t){e=e&&parseFloat(e.replace(",","."));return(isNaN(e)?0:e)*t}function zt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function $t(s,i){return function(e,t){var n;return null===t||isNaN(+t)||(Q(i,"moment()."+i+"(period, number) is deprecated. Please use moment()."+i+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),n=e,e=t,t=n),qt(this,C(e,t),s),this}}function qt(e,t,n,s){var i=t._milliseconds,r=Ht(t._days),t=Ht(t._months);e.isValid()&&(s=null==s||s,t&&Ve(e,ce(e,"Month")+t*n),r&&fe(e,"Date",ce(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s&&f.updateOffset(e,r||t))}C.fn=Ct.prototype,C.invalid=function(){return C(NaN)};Ce=$t(1,"add"),Je=$t(-1,"subtract");function Bt(e){return"string"==typeof e||e instanceof String}function Jt(e){return h(e)||V(e)||Bt(e)||u(e)||function(t){var e=a(t),n=!1;e&&(n=0===t.filter(function(e){return!u(e)&&Bt(t)}).length);return e&&n}(e)||function(e){var t,n,s=F(e)&&!L(e),i=!1,r=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"],a=r.length;for(t=0;t<a;t+=1)n=r[t],i=i||c(e,n);return s&&i}(e)||null==e}function Qt(e,t){if(e.date()<t.date())return-Qt(t,e);var n=12*(t.year()-e.year())+(t.month()-e.month()),s=e.clone().add(n,"months"),t=t-s<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(1+n,"months")-s);return-(n+t)||0}function Xt(e){return void 0===e?this._locale._abbr:(null!=(e=mt(e))&&(this._locale=e),this)}f.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",f.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";Xe=e("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function Kt(){return this._locale}var en=126227808e5;function tn(e,t){return(e%t+t)%t}function nn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-en:new Date(e,t,n).valueOf()}function sn(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-en:Date.UTC(e,t,n)}function rn(e,t){return t.erasAbbrRegex(e)}function an(){for(var e=[],t=[],n=[],s=[],i=this.eras(),r=0,a=i.length;r<a;++r)t.push(M(i[r].name)),e.push(M(i[r].abbr)),n.push(M(i[r].narrow)),s.push(M(i[r].name)),s.push(M(i[r].abbr)),s.push(M(i[r].narrow));this._erasRegex=new RegExp("^("+s.join("|")+")","i"),this._erasNameRegex=new RegExp("^("+t.join("|")+")","i"),this._erasAbbrRegex=new RegExp("^("+e.join("|")+")","i"),this._erasNarrowRegex=new RegExp("^("+n.join("|")+")","i")}function on(e,t){s(0,[e,e.length],0,t)}function un(e,t,n,s,i){var r;return null==e?qe(this,s,i).year:(r=P(e,s,i),function(e,t,n,s,i){e=$e(e,t,n,s,i),t=Ze(e.year,0,e.dayOfYear);return this.year(t.getUTCFullYear()),this.month(t.getUTCMonth()),this.date(t.getUTCDate()),this}.call(this,e,t=r<t?r:t,n,s,i))}s("N",0,0,"eraAbbr"),s("NN",0,0,"eraAbbr"),s("NNN",0,0,"eraAbbr"),s("NNNN",0,0,"eraName"),s("NNNNN",0,0,"eraNarrow"),s("y",["y",1],"yo","eraYear"),s("y",["yy",2],0,"eraYear"),s("y",["yyy",3],0,"eraYear"),s("y",["yyyy",4],0,"eraYear"),v("N",rn),v("NN",rn),v("NNN",rn),v("NNNN",function(e,t){return t.erasNameRegex(e)}),v("NNNNN",function(e,t){return t.erasNarrowRegex(e)}),D(["N","NN","NNN","NNNN","NNNNN"],function(e,t,n,s){s=n._locale.erasParse(e,s,n._strict);s?m(n).era=s:m(n).invalidEra=e}),v("y",Me),v("yy",Me),v("yyy",Me),v("yyyy",Me),v("yo",function(e,t){return t._eraYearOrdinalRegex||Me}),D(["y","yy","yyy","yyyy"],Y),D(["yo"],function(e,t,n,s){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[Y]=n._locale.eraYearOrdinalParse(e,i):t[Y]=parseInt(e,10)}),s(0,["gg",2],0,function(){return this.weekYear()%100}),s(0,["GG",2],0,function(){return this.isoWeekYear()%100}),on("gggg","weekYear"),on("ggggg","weekYear"),on("GGGG","isoWeekYear"),on("GGGGG","isoWeekYear"),t("weekYear","gg"),t("isoWeekYear","GG"),n("weekYear",1),n("isoWeekYear",1),v("G",De),v("g",De),v("GG",p,w),v("gg",p,w),v("GGGG",ke,_e),v("gggg",ke,_e),v("GGGGG",ve,ye),v("ggggg",ve,ye),Te(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=g(e)}),Te(["gg","GG"],function(e,t,n,s){t[s]=f.parseTwoDigitYear(e)}),s("Q",0,"Qo","quarter"),t("quarter","Q"),n("quarter",7),v("Q",i),D("Q",function(e,t){t[O]=3*(g(e)-1)}),s("D",["DD",2],"Do","date"),t("date","D"),n("date",9),v("D",p),v("DD",p,w),v("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),D(["D","DD"],b),D("Do",function(e,t){t[b]=g(e.match(p)[0])});ke=de("Date",!0);s("DDD",["DDDD",3],"DDDo","dayOfYear"),t("dayOfYear","DDD"),n("dayOfYear",4),v("DDD",pe),v("DDDD",me),D(["DDD","DDDD"],function(e,t,n){n._dayOfYear=g(e)}),s("m",["mm",2],0,"minute"),t("minute","m"),n("minute",14),v("m",p),v("mm",p,w),D(["m","mm"],T);var ln,_e=de("Minutes",!1),ve=(s("s",["ss",2],0,"second"),t("second","s"),n("second",15),v("s",p),v("ss",p,w),D(["s","ss"],N),de("Seconds",!1));for(s("S",0,0,function(){return~~(this.millisecond()/100)}),s(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),s(0,["SSS",3],0,"millisecond"),s(0,["SSSS",4],0,function(){return 10*this.millisecond()}),s(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),s(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),s(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),s(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),s(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),t("millisecond","ms"),n("millisecond",16),v("S",pe,i),v("SS",pe,w),v("SSS",pe,me),ln="SSSS";ln.length<=9;ln+="S")v(ln,Me);function hn(e,t){t[Ne]=g(1e3*("0."+e))}for(ln="S";ln.length<=9;ln+="S")D(ln,hn);ye=de("Milliseconds",!1),s("z",0,0,"zoneAbbr"),s("zz",0,0,"zoneName");i=q.prototype;function dn(e){return e}i.add=Ce,i.calendar=function(e,t){1===arguments.length&&(arguments[0]?Jt(arguments[0])?(e=arguments[0],t=void 0):function(e){for(var t=F(e)&&!L(e),n=!1,s=["sameDay","nextDay","lastDay","nextWeek","lastWeek","sameElse"],i=0;i<s.length;i+=1)n=n||c(e,s[i]);return t&&n}(arguments[0])&&(t=arguments[0],e=void 0):t=e=void 0);var e=e||W(),n=Gt(e,this).startOf("day"),n=f.calendarFormat(this,n)||"sameElse",t=t&&(d(t[n])?t[n].call(this,e):t[n]);return this.format(t||this.localeData().calendar(n,this,W(e)))},i.clone=function(){return new q(this)},i.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Gt(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=_(t)){case"year":r=Qt(this,s)/12;break;case"month":r=Qt(this,s);break;case"quarter":r=Qt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:y(r)},i.endOf=function(e){var t,n;if(void 0===(e=_(e))||"millisecond"===e||!this.isValid())return this;switch(n=this._isUTC?sn:nn,e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf(),t+=36e5-tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":t=this._d.valueOf(),t+=6e4-tn(t,6e4)-1;break;case"second":t=this._d.valueOf(),t+=1e3-tn(t,1e3)-1;break}return this._d.setTime(t),f.updateOffset(this,!0),this},i.format=function(e){return e=e||(this.isUtc()?f.defaultFormatUtc:f.defaultFormat),e=re(this,e),this.localeData().postformat(e)},i.from=function(e,t){return this.isValid()&&(h(e)&&e.isValid()||W(e).isValid())?C({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},i.fromNow=function(e){return this.from(W(),e)},i.to=function(e,t){return this.isValid()&&(h(e)&&e.isValid()||W(e).isValid())?C({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},i.toNow=function(e){return this.to(W(),e)},i.get=function(e){return d(this[e=_(e)])?this[e]():this},i.invalidAt=function(){return m(this).overflow},i.isAfter=function(e,t){return e=h(e)?e:W(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()>e.valueOf():e.valueOf()<this.clone().startOf(t).valueOf())},i.isBefore=function(e,t){return e=h(e)?e:W(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()<e.valueOf():this.clone().endOf(t).valueOf()<e.valueOf())},i.isBetween=function(e,t,n,s){return e=h(e)?e:W(e),t=h(t)?t:W(t),!!(this.isValid()&&e.isValid()&&t.isValid())&&(("("===(s=s||"()")[0]?this.isAfter(e,n):!this.isBefore(e,n))&&(")"===s[1]?this.isBefore(t,n):!this.isAfter(t,n)))},i.isSame=function(e,t){var e=h(e)?e:W(e);return!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()===e.valueOf():(e=e.valueOf(),this.clone().startOf(t).valueOf()<=e&&e<=this.clone().endOf(t).valueOf()))},i.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},i.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},i.isValid=function(){return A(this)},i.lang=Xe,i.locale=Xt,i.localeData=Kt,i.max=we,i.min=ge,i.parsingFlags=function(){return E({},m(this))},i.set=function(e,t){if("object"==typeof e)for(var n=function(e){var t,n=[];for(t in e)c(e,t)&&n.push({unit:t,priority:le[t]});return n.sort(function(e,t){return e.priority-t.priority}),n}(e=ue(e)),s=n.length,i=0;i<s;i++)this[n[i].unit](e[n[i].unit]);else if(d(this[e=_(e)]))return this[e](t);return this},i.startOf=function(e){var t,n;if(void 0===(e=_(e))||"millisecond"===e||!this.isValid())return this;switch(n=this._isUTC?sn:nn,e){case"year":t=n(this.year(),0,1);break;case"quarter":t=n(this.year(),this.month()-this.month()%3,1);break;case"month":t=n(this.year(),this.month(),1);break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":t=n(this.year(),this.month(),this.date());break;case"hour":t=this._d.valueOf(),t-=tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":t=this._d.valueOf(),t-=tn(t,6e4);break;case"second":t=this._d.valueOf(),t-=tn(t,1e3);break}return this._d.setTime(t),f.updateOffset(this,!0),this},i.subtract=Je,i.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},i.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},i.toDate=function(){return new Date(this.valueOf())},i.toISOString=function(e){if(!this.isValid())return null;var t=(e=!0!==e)?this.clone().utc():this;return t.year()<0||9999<t.year()?re(t,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):d(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",re(t,"Z")):re(t,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},i.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,t="moment",n="";return this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",n="Z"),t="["+t+'("]',e=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",this.format(t+e+"-MM-DD[T]HH:mm:ss.SSS"+(n+'[")]'))},"undefined"!=typeof Symbol&&null!=Symbol.for&&(i[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),i.toJSON=function(){return this.isValid()?this.toISOString():null},i.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},i.unix=function(){return Math.floor(this.valueOf()/1e3)},i.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},i.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},i.eraName=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].name;if(t[n].until<=e&&e<=t[n].since)return t[n].name}return""},i.eraNarrow=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].narrow;if(t[n].until<=e&&e<=t[n].since)return t[n].narrow}return""},i.eraAbbr=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].abbr;if(t[n].until<=e&&e<=t[n].since)return t[n].abbr}return""},i.eraYear=function(){for(var e,t,n=this.localeData().eras(),s=0,i=n.length;s<i;++s)if(e=n[s].since<=n[s].until?1:-1,t=this.clone().startOf("day").valueOf(),n[s].since<=t&&t<=n[s].until||n[s].until<=t&&t<=n[s].since)return(this.year()-f(n[s].since).year())*e+n[s].offset;return this.year()},i.year=Ie,i.isLeapYear=function(){return he(this.year())},i.weekYear=function(e){return un.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},i.isoWeekYear=function(e){return un.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},i.quarter=i.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},i.month=Ge,i.daysInMonth=function(){return We(this.year(),this.month())},i.week=i.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},i.isoWeek=i.isoWeeks=function(e){var t=qe(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},i.weeksInYear=function(){var e=this.localeData()._week;return P(this.year(),e.dow,e.doy)},i.weeksInWeekYear=function(){var e=this.localeData()._week;return P(this.weekYear(),e.dow,e.doy)},i.isoWeeksInYear=function(){return P(this.year(),1,4)},i.isoWeeksInISOWeekYear=function(){return P(this.isoWeekYear(),1,4)},i.date=ke,i.day=i.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t,n,s=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(t=e,n=this.localeData(),e="string"!=typeof t?t:isNaN(t)?"number"==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10),this.add(e-s,"d")):s},i.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},i.isoWeekday=function(e){return this.isValid()?null!=e?(t=e,n=this.localeData(),n="string"==typeof t?n.weekdaysParse(t)%7||7:isNaN(t)?null:t,this.day(this.day()%7?n:n-7)):this.day()||7:null!=e?this:NaN;var t,n},i.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},i.hour=i.hours=k,i.minute=i.minutes=_e,i.second=i.seconds=ve,i.millisecond=i.milliseconds=ye,i.utcOffset=function(e,t,n){var s,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Et(this);if("string"==typeof e){if(null===(e=Vt(Ye,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(s=Et(this)),this._offset=e,this._isUTC=!0,null!=s&&this.add(s,"m"),i!==e&&(!t||this._changeInProgress?qt(this,C(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,f.updateOffset(this,!0),this._changeInProgress=null)),this},i.utc=function(e){return this.utcOffset(0,e)},i.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Et(this),"m")),this},i.parseZone=function(){var e;return null!=this._tzm?this.utcOffset(this._tzm,!1,!0):"string"==typeof this._i&&(null!=(e=Vt(Se,this._i))?this.utcOffset(e):this.utcOffset(0,!0)),this},i.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?W(e).utcOffset():0,(this.utcOffset()-e)%60==0)},i.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},i.isLocal=function(){return!!this.isValid()&&!this._isUTC},i.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},i.isUtc=At,i.isUTC=At,i.zoneAbbr=function(){return this._isUTC?"UTC":""},i.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},i.dates=e("dates accessor is deprecated. Use date instead.",ke),i.months=e("months accessor is deprecated. Use month instead",Ge),i.years=e("years accessor is deprecated. Use year instead",Ie),i.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?(this.utcOffset(e="string"!=typeof e?-e:e,t),this):-this.utcOffset()}),i.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e,t={};return $(t,this),(t=Nt(t))._a?(e=(t._isUTC?l:W)(t._a),this._isDSTShifted=this.isValid()&&0<function(e,t,n){for(var s=Math.min(e.length,t.length),i=Math.abs(e.length-t.length),r=0,a=0;a<s;a++)(n&&e[a]!==t[a]||!n&&g(e[a])!==g(t[a]))&&r++;return r+i}(t._a,e.toArray())):this._isDSTShifted=!1,this._isDSTShifted});w=K.prototype;function cn(e,t,n,s){var i=mt(),s=l().set(s,t);return i[n](s,e)}function fn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||"",null!=t)return cn(e,t,n,"month");for(var s=[],i=0;i<12;i++)s[i]=cn(e,i,n,"month");return s}function mn(e,t,n,s){t=("boolean"==typeof e?u(t)&&(n=t,t=void 0):(t=e,e=!1,u(n=t)&&(n=t,t=void 0)),t||"");var i,r=mt(),a=e?r._week.dow:0,o=[];if(null!=n)return cn(t,(n+a)%7,s,"day");for(i=0;i<7;i++)o[i]=cn(t,(i+a)%7,s,"day");return o}w.calendar=function(e,t,n){return d(e=this._calendar[e]||this._calendar.sameElse)?e.call(t,n):e},w.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(te).map(function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e}).join(""),this._longDateFormat[e])},w.invalidDate=function(){return this._invalidDate},w.ordinal=function(e){return this._ordinal.replace("%d",e)},w.preparse=dn,w.postformat=dn,w.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return d(i)?i(e,t,n,s):i.replace(/%d/i,e)},w.pastFuture=function(e,t){return d(e=this._relativeTime[0<e?"future":"past"])?e(t):e.replace(/%s/i,t)},w.set=function(e){var t,n;for(n in e)c(e,n)&&(d(t=e[n])?this[n]=t:this["_"+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},w.eras=function(e,t){for(var n,s=this._eras||mt("en")._eras,i=0,r=s.length;i<r;++i){switch(typeof s[i].since){case"string":n=f(s[i].since).startOf("day"),s[i].since=n.valueOf();break}switch(typeof s[i].until){case"undefined":s[i].until=1/0;break;case"string":n=f(s[i].until).startOf("day").valueOf(),s[i].until=n.valueOf();break}}return s},w.erasParse=function(e,t,n){var s,i,r,a,o,u=this.eras();for(e=e.toUpperCase(),s=0,i=u.length;s<i;++s)if(r=u[s].name.toUpperCase(),a=u[s].abbr.toUpperCase(),o=u[s].narrow.toUpperCase(),n)switch(t){case"N":case"NN":case"NNN":if(a===e)return u[s];break;case"NNNN":if(r===e)return u[s];break;case"NNNNN":if(o===e)return u[s];break}else if(0<=[r,a,o].indexOf(e))return u[s]},w.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?f(e.since).year():f(e.since).year()+(t-e.offset)*n},w.erasAbbrRegex=function(e){return c(this,"_erasAbbrRegex")||an.call(this),e?this._erasAbbrRegex:this._erasRegex},w.erasNameRegex=function(e){return c(this,"_erasNameRegex")||an.call(this),e?this._erasNameRegex:this._erasRegex},w.erasNarrowRegex=function(e){return c(this,"_erasNarrowRegex")||an.call(this),e?this._erasNarrowRegex:this._erasRegex},w.months=function(e,t){return e?(a(this._months)?this._months:this._months[(this._months.isFormat||He).test(t)?"format":"standalone"])[e.month()]:a(this._months)?this._months:this._months.standalone},w.monthsShort=function(e,t){return e?(a(this._monthsShort)?this._monthsShort:this._monthsShort[He.test(t)?"format":"standalone"])[e.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},w.monthsParse=function(e,t,n){var s,i;if(this._monthsParseExact)return function(e,t,n){var s,i,r,e=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=l([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=S.call(this._shortMonthsParse,e))?i:null:-1!==(i=S.call(this._longMonthsParse,e))?i:null:"MMM"===t?-1!==(i=S.call(this._shortMonthsParse,e))||-1!==(i=S.call(this._longMonthsParse,e))?i:null:-1!==(i=S.call(this._longMonthsParse,e))||-1!==(i=S.call(this._shortMonthsParse,e))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=l([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(i="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(i.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},w.monthsRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||Ee.call(this),e?this._monthsStrictRegex:this._monthsRegex):(c(this,"_monthsRegex")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},w.monthsShortRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||Ee.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(c(this,"_monthsShortRegex")||(this._monthsShortRegex=Fe),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},w.week=function(e){return qe(e,this._week.dow,this._week.doy).week},w.firstDayOfYear=function(){return this._week.doy},w.firstDayOfWeek=function(){return this._week.dow},w.weekdays=function(e,t){return t=a(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"],!0===e?Be(t,this._week.dow):e?t[e.day()]:t},w.weekdaysMin=function(e){return!0===e?Be(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},w.weekdaysShort=function(e){return!0===e?Be(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},w.weekdaysParse=function(e,t,n){var s,i;if(this._weekdaysParseExact)return function(e,t,n){var s,i,r,e=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=l([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=S.call(this._weekdaysParse,e))?i:null:"ddd"===t?-1!==(i=S.call(this._shortWeekdaysParse,e))?i:null:-1!==(i=S.call(this._minWeekdaysParse,e))?i:null:"dddd"===t?-1!==(i=S.call(this._weekdaysParse,e))||-1!==(i=S.call(this._shortWeekdaysParse,e))||-1!==(i=S.call(this._minWeekdaysParse,e))?i:null:"ddd"===t?-1!==(i=S.call(this._shortWeekdaysParse,e))||-1!==(i=S.call(this._weekdaysParse,e))||-1!==(i=S.call(this._minWeekdaysParse,e))?i:null:-1!==(i=S.call(this._minWeekdaysParse,e))||-1!==(i=S.call(this._weekdaysParse,e))||-1!==(i=S.call(this._shortWeekdaysParse,e))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=l([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[s]||(i="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(i.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},w.weekdaysRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(c(this,"_weekdaysRegex")||(this._weekdaysRegex=Ke),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},w.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(c(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=et),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},w.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(c(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=tt),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},w.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},w.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ct("en",{eras:[{since:"0001-01-01",until:1/0,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===g(e%100/10)?"th":1==t?"st":2==t?"nd":3==t?"rd":"th")}}),f.lang=e("moment.lang is deprecated. Use moment.locale instead.",ct),f.langData=e("moment.langData is deprecated. Use moment.localeData instead.",mt);var _n=Math.abs;function yn(e,t,n,s){t=C(t,n);return e._milliseconds+=s*t._milliseconds,e._days+=s*t._days,e._months+=s*t._months,e._bubble()}function gn(e){return e<0?Math.floor(e):Math.ceil(e)}function wn(e){return 4800*e/146097}function pn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}pe=kn("ms"),me=kn("s"),Ce=kn("m"),we=kn("h"),ge=kn("d"),Je=kn("w"),k=kn("M"),_e=kn("Q"),ve=kn("y");function vn(e){return function(){return this.isValid()?this._data[e]:NaN}}var ye=vn("milliseconds"),ke=vn("seconds"),Ie=vn("minutes"),w=vn("hours"),Mn=vn("days"),Dn=vn("months"),Sn=vn("years");var Yn=Math.round,On={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function bn(e,t,n,s){var i=C(e).abs(),r=Yn(i.as("s")),a=Yn(i.as("m")),o=Yn(i.as("h")),u=Yn(i.as("d")),l=Yn(i.as("M")),h=Yn(i.as("w")),i=Yn(i.as("y")),r=(r<=n.ss?["s",r]:r<n.s&&["ss",r])||a<=1&&["m"]||a<n.m&&["mm",a]||o<=1&&["h"]||o<n.h&&["hh",o]||u<=1&&["d"]||u<n.d&&["dd",u];return(r=(r=null!=n.w?r||h<=1&&["w"]||h<n.w&&["ww",h]:r)||l<=1&&["M"]||l<n.M&&["MM",l]||i<=1&&["y"]||["yy",i])[2]=t,r[3]=0<+e,r[4]=s,function(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}.apply(null,r)}var xn=Math.abs;function Tn(e){return(0<e)-(e<0)||+e}function Nn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,s,i,r,a,o=xn(this._milliseconds)/1e3,u=xn(this._days),l=xn(this._months),h=this.asSeconds();return h?(e=y(o/60),t=y(e/60),o%=60,e%=60,n=y(l/12),l%=12,s=o?o.toFixed(3).replace(/\.?0+$/,""):"",i=Tn(this._months)!==Tn(h)?"-":"",r=Tn(this._days)!==Tn(h)?"-":"",a=Tn(this._milliseconds)!==Tn(h)?"-":"",(h<0?"-":"")+"P"+(n?i+n+"Y":"")+(l?i+l+"M":"")+(u?r+u+"D":"")+(t||e||o?"T":"")+(t?a+t+"H":"")+(e?a+e+"M":"")+(o?a+s+"S":"")):"P0D"}var U=Ct.prototype;return U.isValid=function(){return this._isValid},U.abs=function(){var e=this._data;return this._milliseconds=_n(this._milliseconds),this._days=_n(this._days),this._months=_n(this._months),e.milliseconds=_n(e.milliseconds),e.seconds=_n(e.seconds),e.minutes=_n(e.minutes),e.hours=_n(e.hours),e.months=_n(e.months),e.years=_n(e.years),this},U.add=function(e,t){return yn(this,e,t,1)},U.subtract=function(e,t){return yn(this,e,t,-1)},U.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=_(e))||"quarter"===e||"year"===e)switch(t=this._days+s/864e5,n=this._months+wn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(pn(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},U.asMilliseconds=pe,U.asSeconds=me,U.asMinutes=Ce,U.asHours=we,U.asDays=ge,U.asWeeks=Je,U.asMonths=k,U.asQuarters=_e,U.asYears=ve,U.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*g(this._months/12):NaN},U._bubble=function(){var e=this._milliseconds,t=this._days,n=this._months,s=this._data;return 0<=e&&0<=t&&0<=n||e<=0&&t<=0&&n<=0||(e+=864e5*gn(pn(n)+t),n=t=0),s.milliseconds=e%1e3,e=y(e/1e3),s.seconds=e%60,e=y(e/60),s.minutes=e%60,e=y(e/60),s.hours=e%24,t+=y(e/24),n+=e=y(wn(t)),t-=gn(pn(e)),e=y(n/12),n%=12,s.days=t,s.months=n,s.years=e,this},U.clone=function(){return C(this)},U.get=function(e){return e=_(e),this.isValid()?this[e+"s"]():NaN},U.milliseconds=ye,U.seconds=ke,U.minutes=Ie,U.hours=w,U.days=Mn,U.weeks=function(){return y(this.days()/7)},U.months=Dn,U.years=Sn,U.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n=!1,s=On;return"object"==typeof e&&(t=e,e=!1),"boolean"==typeof e&&(n=e),"object"==typeof t&&(s=Object.assign({},On,t),null!=t.s&&null==t.ss&&(s.ss=t.s-1)),e=this.localeData(),t=bn(this,!n,s,e),n&&(t=e.pastFuture(+this,t)),e.postformat(t)},U.toISOString=Nn,U.toString=Nn,U.toJSON=Nn,U.locale=Xt,U.localeData=Kt,U.toIsoString=e("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Nn),U.lang=Xe,s("X",0,0,"unix"),s("x",0,0,"valueOf"),v("x",De),v("X",/[+-]?\d+(\.\d{1,3})?/),D("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e))}),D("x",function(e,t,n){n._d=new Date(g(e))}),f.version="2.29.4",H=W,f.fn=i,f.min=function(){return Rt("isBefore",[].slice.call(arguments,0))},f.max=function(){return Rt("isAfter",[].slice.call(arguments,0))},f.now=function(){return Date.now?Date.now():+new Date},f.utc=l,f.unix=function(e){return W(1e3*e)},f.months=function(e,t){return fn(e,t,"months")},f.isDate=V,f.locale=ct,f.invalid=I,f.duration=C,f.isMoment=h,f.weekdays=function(e,t,n){return mn(e,t,n,"weekdays")},f.parseZone=function(){return W.apply(null,arguments).parseZone()},f.localeData=mt,f.isDuration=Ut,f.monthsShort=function(e,t){return fn(e,t,"monthsShort")},f.weekdaysMin=function(e,t,n){return mn(e,t,n,"weekdaysMin")},f.defineLocale=ft,f.updateLocale=function(e,t){var n,s;return null!=t?(s=ot,null!=R[e]&&null!=R[e].parentLocale?R[e].set(X(R[e]._config,t)):(t=X(s=null!=(n=dt(e))?n._config:s,t),null==n&&(t.abbr=e),(s=new K(t)).parentLocale=R[e],R[e]=s),ct(e)):null!=R[e]&&(null!=R[e].parentLocale?(R[e]=R[e].parentLocale,e===ct()&&ct(e)):null!=R[e]&&delete R[e]),R[e]},f.locales=function(){return ee(R)},f.weekdaysShort=function(e,t,n){return mn(e,t,n,"weekdaysShort")},f.normalizeUnits=_,f.relativeTimeRounding=function(e){return void 0===e?Yn:"function"==typeof e&&(Yn=e,!0)},f.relativeTimeThreshold=function(e,t){return void 0!==On[e]&&(void 0===t?On[e]:(On[e]=t,"s"===e&&(On.ss=t-1),!0))},f.calendarFormat=function(e,t){return(e=e.diff(t,"days",!0))<-6?"sameElse":e<-1?"lastWeek":e<0?"lastDay":e<1?"sameDay":e<2?"nextDay":e<7?"nextWeek":"sameElse"},f.prototype=i,f.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},f});
//# sourceMappingURL=moment.min.js.map
;
/**
 * 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/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;
});

( function( factory ) {
	"use strict";

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

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

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

// Create a local jQuery because jQuery Color relies on it and the
// global may not exist with AMD and a custom build (#10199).
// This module is a noop if used as a regular AMD module.
// eslint-disable-next-line no-unused-vars
var jQuery = $;

} );

/*!
 * jQuery Color Animations v2.2.0
 * https://github.com/jquery/jquery-color
 *
 * Copyright OpenJS Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * Date: Sun May 10 09:02:36 2020 +0200
 */

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

		// AMD. Register as an anonymous module.
		define( 'jquery/ui-modules/vendor/jquery-color/jquery.color',[ "jquery" ], factory );
	} else if ( typeof exports === "object" ) {
		module.exports = factory( require( "jquery" ) );
	} else {
		factory( root.jQuery );
	}
} )( this, function( jQuery, undefined ) {

	var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " +
		"borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",

	class2type = {},
	toString = class2type.toString,

	// plusequals test for += 100 -= 100
	rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,

	// a set of RE's that can match strings and generate color tuples.
	stringParsers = [ {
			re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			parse: function( execResult ) {
				return [
					execResult[ 1 ],
					execResult[ 2 ],
					execResult[ 3 ],
					execResult[ 4 ]
				];
			}
		}, {
			re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			parse: function( execResult ) {
				return [
					execResult[ 1 ] * 2.55,
					execResult[ 2 ] * 2.55,
					execResult[ 3 ] * 2.55,
					execResult[ 4 ]
				];
			}
		}, {

			// this regex ignores A-F because it's compared against an already lowercased string
			re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/,
			parse: function( execResult ) {
				return [
					parseInt( execResult[ 1 ], 16 ),
					parseInt( execResult[ 2 ], 16 ),
					parseInt( execResult[ 3 ], 16 ),
					execResult[ 4 ] ?
						( parseInt( execResult[ 4 ], 16 ) / 255 ).toFixed( 2 ) :
						1
				];
			}
		}, {

			// this regex ignores A-F because it's compared against an already lowercased string
			re: /#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/,
			parse: function( execResult ) {
				return [
					parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
					parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
					parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ),
					execResult[ 4 ] ?
						( parseInt( execResult[ 4 ] + execResult[ 4 ], 16 ) / 255 )
							.toFixed( 2 ) :
						1
				];
			}
		}, {
			re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			space: "hsla",
			parse: function( execResult ) {
				return [
					execResult[ 1 ],
					execResult[ 2 ] / 100,
					execResult[ 3 ] / 100,
					execResult[ 4 ]
				];
			}
		} ],

	// jQuery.Color( )
	color = jQuery.Color = function( color, green, blue, alpha ) {
		return new jQuery.Color.fn.parse( color, green, blue, alpha );
	},
	spaces = {
		rgba: {
			props: {
				red: {
					idx: 0,
					type: "byte"
				},
				green: {
					idx: 1,
					type: "byte"
				},
				blue: {
					idx: 2,
					type: "byte"
				}
			}
		},

		hsla: {
			props: {
				hue: {
					idx: 0,
					type: "degrees"
				},
				saturation: {
					idx: 1,
					type: "percent"
				},
				lightness: {
					idx: 2,
					type: "percent"
				}
			}
		}
	},
	propTypes = {
		"byte": {
			floor: true,
			max: 255
		},
		"percent": {
			max: 1
		},
		"degrees": {
			mod: 360,
			floor: true
		}
	},
	support = color.support = {},

	// element for support tests
	supportElem = jQuery( "<p>" )[ 0 ],

	// colors = jQuery.Color.names
	colors,

	// local aliases of functions called often
	each = jQuery.each;

// determine rgba support immediately
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;

// define cache name and alpha properties
// for rgba and hsla spaces
each( spaces, function( spaceName, space ) {
	space.cache = "_" + spaceName;
	space.props.alpha = {
		idx: 3,
		type: "percent",
		def: 1
	};
} );

// Populate the class2type map
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
	function( _i, name ) {
		class2type[ "[object " + name + "]" ] = name.toLowerCase();
	} );

function getType( obj ) {
	if ( obj == null ) {
		return obj + "";
	}

	return typeof obj === "object" ?
		class2type[ toString.call( obj ) ] || "object" :
		typeof obj;
}

function clamp( value, prop, allowEmpty ) {
	var type = propTypes[ prop.type ] || {};

	if ( value == null ) {
		return ( allowEmpty || !prop.def ) ? null : prop.def;
	}

	// ~~ is an short way of doing floor for positive numbers
	value = type.floor ? ~~value : parseFloat( value );

	// IE will pass in empty strings as value for alpha,
	// which will hit this case
	if ( isNaN( value ) ) {
		return prop.def;
	}

	if ( type.mod ) {

		// we add mod before modding to make sure that negatives values
		// get converted properly: -10 -> 350
		return ( value + type.mod ) % type.mod;
	}

	// for now all property types without mod have min and max
	return Math.min( type.max, Math.max( 0, value ) );
}

function stringParse( string ) {
	var inst = color(),
		rgba = inst._rgba = [];

	string = string.toLowerCase();

	each( stringParsers, function( _i, parser ) {
		var parsed,
			match = parser.re.exec( string ),
			values = match && parser.parse( match ),
			spaceName = parser.space || "rgba";

		if ( values ) {
			parsed = inst[ spaceName ]( values );

			// if this was an rgba parse the assignment might happen twice
			// oh well....
			inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
			rgba = inst._rgba = parsed._rgba;

			// exit each( stringParsers ) here because we matched
			return false;
		}
	} );

	// Found a stringParser that handled it
	if ( rgba.length ) {

		// if this came from a parsed string, force "transparent" when alpha is 0
		// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
		if ( rgba.join() === "0,0,0,0" ) {
			jQuery.extend( rgba, colors.transparent );
		}
		return inst;
	}

	// named colors
	return colors[ string ];
}

color.fn = jQuery.extend( color.prototype, {
	parse: function( red, green, blue, alpha ) {
		if ( red === undefined ) {
			this._rgba = [ null, null, null, null ];
			return this;
		}
		if ( red.jquery || red.nodeType ) {
			red = jQuery( red ).css( green );
			green = undefined;
		}

		var inst = this,
			type = getType( red ),
			rgba = this._rgba = [];

		// more than 1 argument specified - assume ( red, green, blue, alpha )
		if ( green !== undefined ) {
			red = [ red, green, blue, alpha ];
			type = "array";
		}

		if ( type === "string" ) {
			return this.parse( stringParse( red ) || colors._default );
		}

		if ( type === "array" ) {
			each( spaces.rgba.props, function( _key, prop ) {
				rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
			} );
			return this;
		}

		if ( type === "object" ) {
			if ( red instanceof color ) {
				each( spaces, function( _spaceName, space ) {
					if ( red[ space.cache ] ) {
						inst[ space.cache ] = red[ space.cache ].slice();
					}
				} );
			} else {
				each( spaces, function( _spaceName, space ) {
					var cache = space.cache;
					each( space.props, function( key, prop ) {

						// if the cache doesn't exist, and we know how to convert
						if ( !inst[ cache ] && space.to ) {

							// if the value was null, we don't need to copy it
							// if the key was alpha, we don't need to copy it either
							if ( key === "alpha" || red[ key ] == null ) {
								return;
							}
							inst[ cache ] = space.to( inst._rgba );
						}

						// this is the only case where we allow nulls for ALL properties.
						// call clamp with alwaysAllowEmpty
						inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
					} );

					// everything defined but alpha?
					if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {

						// use the default of 1
						if ( inst[ cache ][ 3 ] == null ) {
							inst[ cache ][ 3 ] = 1;
						}

						if ( space.from ) {
							inst._rgba = space.from( inst[ cache ] );
						}
					}
				} );
			}
			return this;
		}
	},
	is: function( compare ) {
		var is = color( compare ),
			same = true,
			inst = this;

		each( spaces, function( _, space ) {
			var localCache,
				isCache = is[ space.cache ];
			if ( isCache ) {
				localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
				each( space.props, function( _, prop ) {
					if ( isCache[ prop.idx ] != null ) {
						same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
						return same;
					}
				} );
			}
			return same;
		} );
		return same;
	},
	_space: function() {
		var used = [],
			inst = this;
		each( spaces, function( spaceName, space ) {
			if ( inst[ space.cache ] ) {
				used.push( spaceName );
			}
		} );
		return used.pop();
	},
	transition: function( other, distance ) {
		var end = color( other ),
			spaceName = end._space(),
			space = spaces[ spaceName ],
			startColor = this.alpha() === 0 ? color( "transparent" ) : this,
			start = startColor[ space.cache ] || space.to( startColor._rgba ),
			result = start.slice();

		end = end[ space.cache ];
		each( space.props, function( _key, prop ) {
			var index = prop.idx,
				startValue = start[ index ],
				endValue = end[ index ],
				type = propTypes[ prop.type ] || {};

			// if null, don't override start value
			if ( endValue === null ) {
				return;
			}

			// if null - use end
			if ( startValue === null ) {
				result[ index ] = endValue;
			} else {
				if ( type.mod ) {
					if ( endValue - startValue > type.mod / 2 ) {
						startValue += type.mod;
					} else if ( startValue - endValue > type.mod / 2 ) {
						startValue -= type.mod;
					}
				}
				result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
			}
		} );
		return this[ spaceName ]( result );
	},
	blend: function( opaque ) {

		// if we are already opaque - return ourself
		if ( this._rgba[ 3 ] === 1 ) {
			return this;
		}

		var rgb = this._rgba.slice(),
			a = rgb.pop(),
			blend = color( opaque )._rgba;

		return color( jQuery.map( rgb, function( v, i ) {
			return ( 1 - a ) * blend[ i ] + a * v;
		} ) );
	},
	toRgbaString: function() {
		var prefix = "rgba(",
			rgba = jQuery.map( this._rgba, function( v, i ) {
				if ( v != null ) {
					return v;
				}
				return i > 2 ? 1 : 0;
			} );

		if ( rgba[ 3 ] === 1 ) {
			rgba.pop();
			prefix = "rgb(";
		}

		return prefix + rgba.join() + ")";
	},
	toHslaString: function() {
		var prefix = "hsla(",
			hsla = jQuery.map( this.hsla(), function( v, i ) {
				if ( v == null ) {
					v = i > 2 ? 1 : 0;
				}

				// catch 1 and 2
				if ( i && i < 3 ) {
					v = Math.round( v * 100 ) + "%";
				}
				return v;
			} );

		if ( hsla[ 3 ] === 1 ) {
			hsla.pop();
			prefix = "hsl(";
		}
		return prefix + hsla.join() + ")";
	},
	toHexString: function( includeAlpha ) {
		var rgba = this._rgba.slice(),
			alpha = rgba.pop();

		if ( includeAlpha ) {
			rgba.push( ~~( alpha * 255 ) );
		}

		return "#" + jQuery.map( rgba, function( v ) {

			// default to 0 when nulls exist
			v = ( v || 0 ).toString( 16 );
			return v.length === 1 ? "0" + v : v;
		} ).join( "" );
	},
	toString: function() {
		return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
	}
} );
color.fn.parse.prototype = color.fn;

// hsla conversions adapted from:
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021

function hue2rgb( p, q, h ) {
	h = ( h + 1 ) % 1;
	if ( h * 6 < 1 ) {
		return p + ( q - p ) * h * 6;
	}
	if ( h * 2 < 1 ) {
		return q;
	}
	if ( h * 3 < 2 ) {
		return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
	}
	return p;
}

spaces.hsla.to = function( rgba ) {
	if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
		return [ null, null, null, rgba[ 3 ] ];
	}
	var r = rgba[ 0 ] / 255,
		g = rgba[ 1 ] / 255,
		b = rgba[ 2 ] / 255,
		a = rgba[ 3 ],
		max = Math.max( r, g, b ),
		min = Math.min( r, g, b ),
		diff = max - min,
		add = max + min,
		l = add * 0.5,
		h, s;

	if ( min === max ) {
		h = 0;
	} else if ( r === max ) {
		h = ( 60 * ( g - b ) / diff ) + 360;
	} else if ( g === max ) {
		h = ( 60 * ( b - r ) / diff ) + 120;
	} else {
		h = ( 60 * ( r - g ) / diff ) + 240;
	}

	// chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
	// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
	if ( diff === 0 ) {
		s = 0;
	} else if ( l <= 0.5 ) {
		s = diff / add;
	} else {
		s = diff / ( 2 - add );
	}
	return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ];
};

spaces.hsla.from = function( hsla ) {
	if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
		return [ null, null, null, hsla[ 3 ] ];
	}
	var h = hsla[ 0 ] / 360,
		s = hsla[ 1 ],
		l = hsla[ 2 ],
		a = hsla[ 3 ],
		q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
		p = 2 * l - q;

	return [
		Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
		Math.round( hue2rgb( p, q, h ) * 255 ),
		Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
		a
	];
};


each( spaces, function( spaceName, space ) {
	var props = space.props,
		cache = space.cache,
		to = space.to,
		from = space.from;

	// makes rgba() and hsla()
	color.fn[ spaceName ] = function( value ) {

		// generate a cache for this space if it doesn't exist
		if ( to && !this[ cache ] ) {
			this[ cache ] = to( this._rgba );
		}
		if ( value === undefined ) {
			return this[ cache ].slice();
		}

		var ret,
			type = getType( value ),
			arr = ( type === "array" || type === "object" ) ? value : arguments,
			local = this[ cache ].slice();

		each( props, function( key, prop ) {
			var val = arr[ type === "object" ? key : prop.idx ];
			if ( val == null ) {
				val = local[ prop.idx ];
			}
			local[ prop.idx ] = clamp( val, prop );
		} );

		if ( from ) {
			ret = color( from( local ) );
			ret[ cache ] = local;
			return ret;
		} else {
			return color( local );
		}
	};

	// makes red() green() blue() alpha() hue() saturation() lightness()
	each( props, function( key, prop ) {

		// alpha is included in more than one space
		if ( color.fn[ key ] ) {
			return;
		}
		color.fn[ key ] = function( value ) {
			var local, cur, match, fn,
				vtype = getType( value );

			if ( key === "alpha" ) {
				fn = this._hsla ? "hsla" : "rgba";
			} else {
				fn = spaceName;
			}
			local = this[ fn ]();
			cur = local[ prop.idx ];

			if ( vtype === "undefined" ) {
				return cur;
			}

			if ( vtype === "function" ) {
				value = value.call( this, cur );
				vtype = getType( value );
			}
			if ( value == null && prop.empty ) {
				return this;
			}
			if ( vtype === "string" ) {
				match = rplusequals.exec( value );
				if ( match ) {
					value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
				}
			}
			local[ prop.idx ] = value;
			return this[ fn ]( local );
		};
	} );
} );

// add cssHook and .fx.step function for each named hook.
// accept a space separated string of properties
color.hook = function( hook ) {
	var hooks = hook.split( " " );
	each( hooks, function( _i, hook ) {
		jQuery.cssHooks[ hook ] = {
			set: function( elem, value ) {
				var parsed, curElem,
					backgroundColor = "";

				if ( value !== "transparent" && ( getType( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
					value = color( parsed || value );
					if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
						curElem = hook === "backgroundColor" ? elem.parentNode : elem;
						while (
							( backgroundColor === "" || backgroundColor === "transparent" ) &&
							curElem && curElem.style
						) {
							try {
								backgroundColor = jQuery.css( curElem, "backgroundColor" );
								curElem = curElem.parentNode;
							} catch ( e ) {
							}
						}

						value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
							backgroundColor :
							"_default" );
					}

					value = value.toRgbaString();
				}
				try {
					elem.style[ hook ] = value;
				} catch ( e ) {

					// wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
				}
			}
		};
		jQuery.fx.step[ hook ] = function( fx ) {
			if ( !fx.colorInit ) {
				fx.start = color( fx.elem, hook );
				fx.end = color( fx.end );
				fx.colorInit = true;
			}
			jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
		};
	} );

};

color.hook( stepHooks );

jQuery.cssHooks.borderColor = {
	expand: function( value ) {
		var expanded = {};

		each( [ "Top", "Right", "Bottom", "Left" ], function( _i, part ) {
			expanded[ "border" + part + "Color" ] = value;
		} );
		return expanded;
	}
};

// Basic color names only.
// Usage of any of the other color names requires adding yourself or including
// jquery.color.svg-names.js.
colors = jQuery.Color.names = {

	// 4.1. Basic color keywords
	aqua: "#00ffff",
	black: "#000000",
	blue: "#0000ff",
	fuchsia: "#ff00ff",
	gray: "#808080",
	green: "#008000",
	lime: "#00ff00",
	maroon: "#800000",
	navy: "#000080",
	olive: "#808000",
	purple: "#800080",
	red: "#ff0000",
	silver: "#c0c0c0",
	teal: "#008080",
	white: "#ffffff",
	yellow: "#ffff00",

	// 4.2.3. "transparent" color keyword
	transparent: [ null, null, null, 0 ],

	_default: "#ffffff"
};

} );

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

//>>label: Effects Core
//>>group: Effects
/* eslint-disable max-len */
//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
/* eslint-enable max-len */
//>>docs: http://api.jqueryui.com/category/effects-core/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/effect',[
            "jquery",
            "./jquery-var-for-color",
            "./vendor/jquery-color/jquery.color",
            "./version"
        ], factory );
    } else {

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

    var dataSpace = "ui-effects-",
        dataSpaceStyle = "ui-effects-style",
        dataSpaceAnimated = "ui-effects-animated";

    $.effects = {
        effect: {}
    };

    /******************************************************************************/
    /****************************** CLASS ANIMATIONS ******************************/
    /******************************************************************************/
    ( function() {

        var classAnimationActions = [ "add", "remove", "toggle" ],
            shorthandStyles = {
                border: 1,
                borderBottom: 1,
                borderColor: 1,
                borderLeft: 1,
                borderRight: 1,
                borderTop: 1,
                borderWidth: 1,
                margin: 1,
                padding: 1
            };

        $.each(
            [ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ],
            function( _, prop ) {
                $.fx.step[ prop ] = function( fx ) {
                    if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
                        jQuery.style( fx.elem, prop, fx.end );
                        fx.setAttr = true;
                    }
                };
            }
        );

        function camelCase( string ) {
            return string.replace( /-([\da-z])/gi, function( all, letter ) {
                return letter.toUpperCase();
            } );
        }

        function getElementStyles( elem ) {
            var key, len,
                style = elem.ownerDocument.defaultView ?
                    elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
                    elem.currentStyle,
                styles = {};

            if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
                len = style.length;
                while ( len-- ) {
                    key = style[ len ];
                    if ( typeof style[ key ] === "string" ) {
                        styles[ camelCase( key ) ] = style[ key ];
                    }
                }

                // Support: Opera, IE <9
            } else {
                for ( key in style ) {
                    if ( typeof style[ key ] === "string" ) {
                        styles[ key ] = style[ key ];
                    }
                }
            }

            return styles;
        }

        function styleDifference( oldStyle, newStyle ) {
            var diff = {},
                name, value;

            for ( name in newStyle ) {
                value = newStyle[ name ];
                if ( oldStyle[ name ] !== value ) {
                    if ( !shorthandStyles[ name ] ) {
                        if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
                            diff[ name ] = value;
                        }
                    }
                }
            }

            return diff;
        }

// Support: jQuery <1.8
        if ( !$.fn.addBack ) {
            $.fn.addBack = function( selector ) {
                return this.add( selector == null ?
                    this.prevObject : this.prevObject.filter( selector )
                );
            };
        }

        $.effects.animateClass = function( value, duration, easing, callback ) {
            var o = $.speed( duration, easing, callback );

            return this.queue( function() {
                var animated = $( this ),
                    baseClass = animated.attr( "class" ) || "",
                    applyClassChange,
                    allAnimations = o.children ? animated.find( "*" ).addBack() : animated;

                // Map the animated objects to store the original styles.
                allAnimations = allAnimations.map( function() {
                    var el = $( this );
                    return {
                        el: el,
                        start: getElementStyles( this )
                    };
                } );

                // Apply class change
                applyClassChange = function() {
                    $.each( classAnimationActions, function( i, action ) {
                        if ( value[ action ] ) {
                            animated[ action + "Class" ]( value[ action ] );
                        }
                    } );
                };
                applyClassChange();

                // Map all animated objects again - calculate new styles and diff
                allAnimations = allAnimations.map( function() {
                    this.end = getElementStyles( this.el[ 0 ] );
                    this.diff = styleDifference( this.start, this.end );
                    return this;
                } );

                // Apply original class
                animated.attr( "class", baseClass );

                // Map all animated objects again - this time collecting a promise
                allAnimations = allAnimations.map( function() {
                    var styleInfo = this,
                        dfd = $.Deferred(),
                        opts = $.extend( {}, o, {
                            queue: false,
                            complete: function() {
                                dfd.resolve( styleInfo );
                            }
                        } );

                    this.el.animate( this.diff, opts );
                    return dfd.promise();
                } );

                // Once all animations have completed:
                $.when.apply( $, allAnimations.get() ).done( function() {

                    // Set the final class
                    applyClassChange();

                    // For each animated element,
                    // clear all css properties that were animated
                    $.each( arguments, function() {
                        var el = this.el;
                        $.each( this.diff, function( key ) {
                            el.css( key, "" );
                        } );
                    } );

                    // This is guarnteed to be there if you use jQuery.speed()
                    // it also handles dequeuing the next anim...
                    o.complete.call( animated[ 0 ] );
                } );
            } );
        };

        $.fn.extend( {
            addClass: ( function( orig ) {
                return function( classNames, speed, easing, callback ) {
                    return speed ?
                        $.effects.animateClass.call( this,
                            { add: classNames }, speed, easing, callback ) :
                        orig.apply( this, arguments );
                };
            } )( $.fn.addClass ),

            removeClass: ( function( orig ) {
                return function( classNames, speed, easing, callback ) {
                    return arguments.length > 1 ?
                        $.effects.animateClass.call( this,
                            { remove: classNames }, speed, easing, callback ) :
                        orig.apply( this, arguments );
                };
            } )( $.fn.removeClass ),

            toggleClass: ( function( orig ) {
                return function( classNames, force, speed, easing, callback ) {
                    if ( typeof force === "boolean" || force === undefined ) {
                        if ( !speed ) {

                            // Without speed parameter
                            return orig.apply( this, arguments );
                        } else {
                            return $.effects.animateClass.call( this,
                                ( force ? { add: classNames } : { remove: classNames } ),
                                speed, easing, callback );
                        }
                    } else {

                        // Without force parameter
                        return $.effects.animateClass.call( this,
                            { toggle: classNames }, force, speed, easing );
                    }
                };
            } )( $.fn.toggleClass ),

            switchClass: function( remove, add, speed, easing, callback ) {
                return $.effects.animateClass.call( this, {
                    add: add,
                    remove: remove
                }, speed, easing, callback );
            }
        } );

    } )();

    /******************************************************************************/
    /*********************************** EFFECTS **********************************/
    /******************************************************************************/

    ( function() {

        if ( $.expr && $.expr.pseudos && $.expr.pseudos.animated ) {
            $.expr.pseudos.animated = ( function( orig ) {
                return function( elem ) {
                    return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
                };
            } )( $.expr.pseudos.animated );
        }

        if ( $.uiBackCompat !== false ) {
            $.extend( $.effects, {

                // Saves a set of properties in a data storage
                save: function( element, set ) {
                    var i = 0, length = set.length;
                    for ( ; i < length; i++ ) {
                        if ( set[ i ] !== null ) {
                            element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
                        }
                    }
                },

                // Restores a set of previously saved properties from a data storage
                restore: function( element, set ) {
                    var val, i = 0, length = set.length;
                    for ( ; i < length; i++ ) {
                        if ( set[ i ] !== null ) {
                            val = element.data( dataSpace + set[ i ] );
                            element.css( set[ i ], val );
                        }
                    }
                },

                setMode: function( el, mode ) {
                    if ( mode === "toggle" ) {
                        mode = el.is( ":hidden" ) ? "show" : "hide";
                    }
                    return mode;
                },

                // Wraps the element around a wrapper that copies position properties
                createWrapper: function( element ) {

                    // If the element is already wrapped, return it
                    if ( element.parent().is( ".ui-effects-wrapper" ) ) {
                        return element.parent();
                    }

                    // Wrap the element
                    var props = {
                            width: element.outerWidth( true ),
                            height: element.outerHeight( true ),
                            "float": element.css( "float" )
                        },
                        wrapper = $( "<div></div>" )
                            .addClass( "ui-effects-wrapper" )
                            .css( {
                                fontSize: "100%",
                                background: "transparent",
                                border: "none",
                                margin: 0,
                                padding: 0
                            } ),

                        // Store the size in case width/height are defined in % - Fixes #5245
                        size = {
                            width: element.width(),
                            height: element.height()
                        },
                        active = document.activeElement;

                    // Support: Firefox
                    // Firefox incorrectly exposes anonymous content
                    // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
                    try {
                        // eslint-disable-next-line no-unused-expressions
                        active.id;
                    } catch ( e ) {
                        active = document.body;
                    }

                    element.wrap( wrapper );

                    // Fixes #7595 - Elements lose focus when wrapped.
                    if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
                        $( active ).trigger( "focus" );
                    }

                    // Hotfix for jQuery 1.4 since some change in wrap() seems to actually
                    // lose the reference to the wrapped element
                    wrapper = element.parent();

                    // Transfer positioning properties to the wrapper
                    if ( element.css( "position" ) === "static" ) {
                        wrapper.css( { position: "relative" } );
                        element.css( { position: "relative" } );
                    } else {
                        $.extend( props, {
                            position: element.css( "position" ),
                            zIndex: element.css( "z-index" )
                        } );
                        $.each( [ "top", "left", "bottom", "right" ], function( i, pos ) {
                            props[ pos ] = element.css( pos );
                            if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
                                props[ pos ] = "auto";
                            }
                        } );
                        element.css( {
                            position: "relative",
                            top: 0,
                            left: 0,
                            right: "auto",
                            bottom: "auto"
                        } );
                    }
                    element.css( size );

                    return wrapper.css( props ).show();
                },

                removeWrapper: function( element ) {
                    var active = document.activeElement;

                    if ( element.parent().is( ".ui-effects-wrapper" ) ) {
                        element.parent().replaceWith( element );

                        // Fixes #7595 - Elements lose focus when wrapped.
                        if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
                            $( active ).trigger( "focus" );
                        }
                    }

                    return element;
                }
            } );
        }

        $.extend( $.effects, {
            version: "1.13.2",

            define: function( name, mode, effect ) {
                if ( !effect ) {
                    effect = mode;
                    mode = "effect";
                }

                $.effects.effect[ name ] = effect;
                $.effects.effect[ name ].mode = mode;

                return effect;
            },

            scaledDimensions: function( element, percent, direction ) {
                if ( percent === 0 ) {
                    return {
                        height: 0,
                        width: 0,
                        outerHeight: 0,
                        outerWidth: 0
                    };
                }

                var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
                    y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;

                return {
                    height: element.height() * y,
                    width: element.width() * x,
                    outerHeight: element.outerHeight() * y,
                    outerWidth: element.outerWidth() * x
                };

            },

            clipToBox: function( animation ) {
                return {
                    width: animation.clip.right - animation.clip.left,
                    height: animation.clip.bottom - animation.clip.top,
                    left: animation.clip.left,
                    top: animation.clip.top
                };
            },

            // Injects recently queued functions to be first in line (after "inprogress")
            unshift: function( element, queueLength, count ) {
                var queue = element.queue();

                if ( queueLength > 1 ) {
                    queue.splice.apply( queue,
                        [ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
                }
                element.dequeue();
            },

            saveStyle: function( element ) {
                element.data( dataSpaceStyle, element[ 0 ].style.cssText );
            },

            restoreStyle: function( element ) {
                element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
                element.removeData( dataSpaceStyle );
            },

            mode: function( element, mode ) {
                var hidden = element.is( ":hidden" );

                if ( mode === "toggle" ) {
                    mode = hidden ? "show" : "hide";
                }
                if ( hidden ? mode === "hide" : mode === "show" ) {
                    mode = "none";
                }
                return mode;
            },

            // Translates a [top,left] array into a baseline value
            getBaseline: function( origin, original ) {
                var y, x;

                switch ( origin[ 0 ] ) {
                    case "top":
                        y = 0;
                        break;
                    case "middle":
                        y = 0.5;
                        break;
                    case "bottom":
                        y = 1;
                        break;
                    default:
                        y = origin[ 0 ] / original.height;
                }

                switch ( origin[ 1 ] ) {
                    case "left":
                        x = 0;
                        break;
                    case "center":
                        x = 0.5;
                        break;
                    case "right":
                        x = 1;
                        break;
                    default:
                        x = origin[ 1 ] / original.width;
                }

                return {
                    x: x,
                    y: y
                };
            },

            // Creates a placeholder element so that the original element can be made absolute
            createPlaceholder: function( element ) {
                var placeholder,
                    cssPosition = element.css( "position" ),
                    position = element.position();

                // Lock in margins first to account for form elements, which
                // will change margin if you explicitly set height
                // see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
                // Support: Safari
                element.css( {
                    marginTop: element.css( "marginTop" ),
                    marginBottom: element.css( "marginBottom" ),
                    marginLeft: element.css( "marginLeft" ),
                    marginRight: element.css( "marginRight" )
                } )
                    .outerWidth( element.outerWidth() )
                    .outerHeight( element.outerHeight() );

                if ( /^(static|relative)/.test( cssPosition ) ) {
                    cssPosition = "absolute";

                    placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( {

                        // Convert inline to inline block to account for inline elements
                        // that turn to inline block based on content (like img)
                        display: /^(inline|ruby)/.test( element.css( "display" ) ) ?
                            "inline-block" :
                            "block",
                        visibility: "hidden",

                        // Margins need to be set to account for margin collapse
                        marginTop: element.css( "marginTop" ),
                        marginBottom: element.css( "marginBottom" ),
                        marginLeft: element.css( "marginLeft" ),
                        marginRight: element.css( "marginRight" ),
                        "float": element.css( "float" )
                    } )
                        .outerWidth( element.outerWidth() )
                        .outerHeight( element.outerHeight() )
                        .addClass( "ui-effects-placeholder" );

                    element.data( dataSpace + "placeholder", placeholder );
                }

                element.css( {
                    position: cssPosition,
                    left: position.left,
                    top: position.top
                } );

                return placeholder;
            },

            removePlaceholder: function( element ) {
                var dataKey = dataSpace + "placeholder",
                    placeholder = element.data( dataKey );

                if ( placeholder ) {
                    placeholder.remove();
                    element.removeData( dataKey );
                }
            },

            // Removes a placeholder if it exists and restores
            // properties that were modified during placeholder creation
            cleanUp: function( element ) {
                $.effects.restoreStyle( element );
                $.effects.removePlaceholder( element );
            },

            setTransition: function( element, list, factor, value ) {
                value = value || {};
                $.each( list, function( i, x ) {
                    var unit = element.cssUnit( x );
                    if ( unit[ 0 ] > 0 ) {
                        value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
                    }
                } );
                return value;
            }
        } );

// Return an effect options object for the given parameters:
        function _normalizeArguments( effect, options, speed, callback ) {

            // Allow passing all options as the first parameter
            if ( $.isPlainObject( effect ) ) {
                options = effect;
                effect = effect.effect;
            }

            // Convert to an object
            effect = { effect: effect };

            // Catch (effect, null, ...)
            if ( options == null ) {
                options = {};
            }

            // Catch (effect, callback)
            if ( typeof options === "function" ) {
                callback = options;
                speed = null;
                options = {};
            }

            // Catch (effect, speed, ?)
            if ( typeof options === "number" || $.fx.speeds[ options ] ) {
                callback = speed;
                speed = options;
                options = {};
            }

            // Catch (effect, options, callback)
            if ( typeof speed === "function" ) {
                callback = speed;
                speed = null;
            }

            // Add options to effect
            if ( options ) {
                $.extend( effect, options );
            }

            speed = speed || options.duration;
            effect.duration = $.fx.off ? 0 :
                typeof speed === "number" ? speed :
                    speed in $.fx.speeds ? $.fx.speeds[ speed ] :
                        $.fx.speeds._default;

            effect.complete = callback || options.complete;

            return effect;
        }

        function standardAnimationOption( option ) {

            // Valid standard speeds (nothing, number, named speed)
            if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
                return true;
            }

            // Invalid strings - treat as "normal" speed
            if ( typeof option === "string" && !$.effects.effect[ option ] ) {
                return true;
            }

            // Complete callback
            if ( typeof option === "function" ) {
                return true;
            }

            // Options hash (but not naming an effect)
            if ( typeof option === "object" && !option.effect ) {
                return true;
            }

            // Didn't match any standard API
            return false;
        }

        $.fn.extend( {
            effect: function( /* effect, options, speed, callback */ ) {
                var args = _normalizeArguments.apply( this, arguments ),
                    effectMethod = $.effects.effect[ args.effect ],
                    defaultMode = effectMethod.mode,
                    queue = args.queue,
                    queueName = queue || "fx",
                    complete = args.complete,
                    mode = args.mode,
                    modes = [],
                    prefilter = function( next ) {
                        var el = $( this ),
                            normalizedMode = $.effects.mode( el, mode ) || defaultMode;

                        // Sentinel for duck-punching the :animated pseudo-selector
                        el.data( dataSpaceAnimated, true );

                        // Save effect mode for later use,
                        // we can't just call $.effects.mode again later,
                        // as the .show() below destroys the initial state
                        modes.push( normalizedMode );

                        // See $.uiBackCompat inside of run() for removal of defaultMode in 1.14
                        if ( defaultMode && ( normalizedMode === "show" ||
                            ( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
                            el.show();
                        }

                        if ( !defaultMode || normalizedMode !== "none" ) {
                            $.effects.saveStyle( el );
                        }

                        if ( typeof next === "function" ) {
                            next();
                        }
                    };

                if ( $.fx.off || !effectMethod ) {

                    // Delegate to the original method (e.g., .show()) if possible
                    if ( mode ) {
                        return this[ mode ]( args.duration, complete );
                    } else {
                        return this.each( function() {
                            if ( complete ) {
                                complete.call( this );
                            }
                        } );
                    }
                }

                function run( next ) {
                    var elem = $( this );

                    function cleanup() {
                        elem.removeData( dataSpaceAnimated );

                        $.effects.cleanUp( elem );

                        if ( args.mode === "hide" ) {
                            elem.hide();
                        }

                        done();
                    }

                    function done() {
                        if ( typeof complete === "function" ) {
                            complete.call( elem[ 0 ] );
                        }

                        if ( typeof next === "function" ) {
                            next();
                        }
                    }

                    // Override mode option on a per element basis,
                    // as toggle can be either show or hide depending on element state
                    args.mode = modes.shift();

                    if ( $.uiBackCompat !== false && !defaultMode ) {
                        if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {

                            // Call the core method to track "olddisplay" properly
                            elem[ mode ]();
                            done();
                        } else {
                            effectMethod.call( elem[ 0 ], args, done );
                        }
                    } else {
                        if ( args.mode === "none" ) {

                            // Call the core method to track "olddisplay" properly
                            elem[ mode ]();
                            done();
                        } else {
                            effectMethod.call( elem[ 0 ], args, cleanup );
                        }
                    }
                }

                // Run prefilter on all elements first to ensure that
                // any showing or hiding happens before placeholder creation,
                // which ensures that any layout changes are correctly captured.
                return queue === false ?
                    this.each( prefilter ).each( run ) :
                    this.queue( queueName, prefilter ).queue( queueName, run );
            },

            show: ( function( orig ) {
                return function( option ) {
                    if ( standardAnimationOption( option ) ) {
                        return orig.apply( this, arguments );
                    } else {
                        var args = _normalizeArguments.apply( this, arguments );
                        args.mode = "show";
                        return this.effect.call( this, args );
                    }
                };
            } )( $.fn.show ),

            hide: ( function( orig ) {
                return function( option ) {
                    if ( standardAnimationOption( option ) ) {
                        return orig.apply( this, arguments );
                    } else {
                        var args = _normalizeArguments.apply( this, arguments );
                        args.mode = "hide";
                        return this.effect.call( this, args );
                    }
                };
            } )( $.fn.hide ),

            toggle: ( function( orig ) {
                return function( option ) {
                    if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
                        return orig.apply( this, arguments );
                    } else {
                        var args = _normalizeArguments.apply( this, arguments );
                        args.mode = "toggle";
                        return this.effect.call( this, args );
                    }
                };
            } )( $.fn.toggle ),

            cssUnit: function( key ) {
                var style = this.css( key ),
                    val = [];

                $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
                    if ( style.indexOf( unit ) > 0 ) {
                        val = [ parseFloat( style ), unit ];
                    }
                } );
                return val;
            },

            cssClip: function( clipObj ) {
                if ( clipObj ) {
                    return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " +
                        clipObj.bottom + "px " + clipObj.left + "px)" );
                }
                return parseClip( this.css( "clip" ), this );
            },

            transfer: function( options, done ) {
                var element = $( this ),
                    target = $( options.to ),
                    targetFixed = target.css( "position" ) === "fixed",
                    body = $( "body" ),
                    fixTop = targetFixed ? body.scrollTop() : 0,
                    fixLeft = targetFixed ? body.scrollLeft() : 0,
                    endPosition = target.offset(),
                    animation = {
                        top: endPosition.top - fixTop,
                        left: endPosition.left - fixLeft,
                        height: target.innerHeight(),
                        width: target.innerWidth()
                    },
                    startPosition = element.offset(),
                    transfer = $( "<div class='ui-effects-transfer'></div>" );

                transfer
                    .appendTo( "body" )
                    .addClass( options.className )
                    .css( {
                        top: startPosition.top - fixTop,
                        left: startPosition.left - fixLeft,
                        height: element.innerHeight(),
                        width: element.innerWidth(),
                        position: targetFixed ? "fixed" : "absolute"
                    } )
                    .animate( animation, options.duration, options.easing, function() {
                        transfer.remove();
                        if ( typeof done === "function" ) {
                            done();
                        }
                    } );
            }
        } );

        function parseClip( str, element ) {
            var outerWidth = element.outerWidth(),
                outerHeight = element.outerHeight(),
                clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
                values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];

            return {
                top: parseFloat( values[ 1 ] ) || 0,
                right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
                bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
                left: parseFloat( values[ 4 ] ) || 0
            };
        }

        $.fx.step.clip = function( fx ) {
            if ( !fx.clipInit ) {
                fx.start = $( fx.elem ).cssClip();
                if ( typeof fx.end === "string" ) {
                    fx.end = parseClip( fx.end, fx.elem );
                }
                fx.clipInit = true;
            }

            $( fx.elem ).cssClip( {
                top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,
                right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,
                bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,
                left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left
            } );
        };

    } )();

    /******************************************************************************/
    /*********************************** EASING ***********************************/
    /******************************************************************************/

    ( function() {

// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)

        var baseEasings = {};

        $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
            baseEasings[ name ] = function( p ) {
                return Math.pow( p, i + 2 );
            };
        } );

        $.extend( baseEasings, {
            Sine: function( p ) {
                return 1 - Math.cos( p * Math.PI / 2 );
            },
            Circ: function( p ) {
                return 1 - Math.sqrt( 1 - p * p );
            },
            Elastic: function( p ) {
                return p === 0 || p === 1 ? p :
                    -Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );
            },
            Back: function( p ) {
                return p * p * ( 3 * p - 2 );
            },
            Bounce: function( p ) {
                var pow2,
                    bounce = 4;

                while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
                return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
            }
        } );

        $.each( baseEasings, function( name, easeIn ) {
            $.easing[ "easeIn" + name ] = easeIn;
            $.easing[ "easeOut" + name ] = function( p ) {
                return 1 - easeIn( 1 - p );
            };
            $.easing[ "easeInOut" + name ] = function( p ) {
                return p < 0.5 ?
                    easeIn( p * 2 ) / 2 :
                    1 - easeIn( p * -2 + 2 ) / 2;
            };
        } );

    } )();

    return $.effects;

} );

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

//>>label: Bounce Effect
//>>group: Effects
//>>description: Bounces an element horizontally or vertically n times.
//>>docs: http://api.jqueryui.com/bounce-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "bounce", function( options, done ) {
        var upAnim, downAnim, refValue,
            element = $( this ),

            // Defaults:
            mode = options.mode,
            hide = mode === "hide",
            show = mode === "show",
            direction = options.direction || "up",
            distance = options.distance,
            times = options.times || 5,

            // Number of internal animations
            anims = times * 2 + ( show || hide ? 1 : 0 ),
            speed = options.duration / anims,
            easing = options.easing,

            // Utility:
            ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
            motion = ( direction === "up" || direction === "left" ),
            i = 0,

            queuelen = element.queue().length;

        $.effects.createPlaceholder( element );

        refValue = element.css( ref );

        // Default distance for the BIGGEST bounce is the outer Distance / 3
        if ( !distance ) {
            distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
        }

        if ( show ) {
            downAnim = { opacity: 1 };
            downAnim[ ref ] = refValue;

            // If we are showing, force opacity 0 and set the initial position
            // then do the "first" animation
            element
                .css( "opacity", 0 )
                .css( ref, motion ? -distance * 2 : distance * 2 )
                .animate( downAnim, speed, easing );
        }

        // Start at the smallest distance if we are hiding
        if ( hide ) {
            distance = distance / Math.pow( 2, times - 1 );
        }

        downAnim = {};
        downAnim[ ref ] = refValue;

        // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
        for ( ; i < times; i++ ) {
            upAnim = {};
            upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;

            element
                .animate( upAnim, speed, easing )
                .animate( downAnim, speed, easing );

            distance = hide ? distance * 2 : distance / 2;
        }

        // Last Bounce when Hiding
        if ( hide ) {
            upAnim = { opacity: 0 };
            upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;

            element.animate( upAnim, speed, easing );
        }

        element.queue( done );

        $.effects.unshift( element, queuelen, anims + 1 );
    } );

} );

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

//>>label: Highlight Effect
//>>group: Effects
//>>description: Highlights the background of an element in a defined color for a custom duration.
//>>docs: http://api.jqueryui.com/highlight-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "highlight", "show", function( options, done ) {
        var element = $( this ),
            animation = {
                backgroundColor: element.css( "backgroundColor" )
            };

        if ( options.mode === "hide" ) {
            animation.opacity = 0;
        }

        $.effects.saveStyle( element );

        element
            .css( {
                backgroundImage: "none",
                backgroundColor: options.color || "#ffff99"
            } )
            .animate( animation, {
                queue: false,
                duration: options.duration,
                easing: options.easing,
                complete: done
            } );
    } );

} );

/**
 * 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('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_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);
                    });
                });
            });
        }
    });
});


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'
    };
});

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

//>>label: Fade Effect
//>>group: Effects
//>>description: Fades the element.
//>>docs: http://api.jqueryui.com/fade-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "fade", "toggle", function( options, done ) {
        var show = options.mode === "show";

        $( this )
            .css( "opacity", show ? 0 : 1 )
            .animate( {
                opacity: show ? 1 : 0
            }, {
                queue: false,
                duration: options.duration,
                easing: options.easing,
                complete: done
            } );
    } );

} );

/**
 * 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));
        }
    };
});


/**
 * Cookie Data Provider Logic
 */
define('Amasty_GdprFrontendUi/js/model/cookie-data-provider',[
    'jquery',
    'mage/url',
], function ($, urlBuilder) {
    'use strict';

    urlBuilder.setBaseUrl(window.BASE_URL);

    return {
        cookieData: [],
        updateResult: $.Deferred(),
        updateStatus: 'done',
        cookieFetchUrl: urlBuilder.build('amcookie/cookie/cookies'),

        getCookieData: function () {
            if (this.cookieData.length === 0) {
                return this.updateCookieData();
            }

            return $.Deferred().resolve(this.cookieData);
        },

        updateCookieData: function () {
            if (this.updateStatus === 'pending') {
                return this.updateResult;
            }

            this.updateStatus = 'pending';
            this.updateResult = $.Deferred();

            $.ajax({
                url: this.cookieFetchUrl,
                async: false,
                type: 'GET',
                cache: true,
                dataType: 'json',
                data: {
                    allowed: $.cookie('amcookie_allowed'),
                    restriction: $.cookie('amcookie_policy_restriction')
                },
                success: function (cookieData) {
                    this.updateStatus = 'done';

                    if (cookieData.cookiePolicy !== undefined) {
                        $.cookie('amcookie_policy_restriction', cookieData.cookiePolicy, {expires: 10, secure: true});
                    }

                    if (cookieData.cookiePolicy === 'allowed') {
                        this.cookieData = cookieData;
                        this.updateResult.resolve(this.cookieData);
                    } else {
                        this.updateResult.reject();
                    }
                }.bind(this)
            });

            return this.updateResult;
        }
    }
});

/**
 * Cookie Storage
 */

define('Amasty_GdprFrontendUi/js/storage/cookie',[], function () {
    'use strict';

    return {
        /**
         * Set Cookie
         * @param {string} name
         * @param {string} value
         * @param {Object} options
         */
        set: function (name, value, options) {
            var updatedCookie = encodeURIComponent(name) + '=' + encodeURIComponent(value),
                optionKey,
                optionValue;

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

            if (options.expires) {
                options.expires = options.expires.toUTCString();
            }


            for (optionKey in options) {
                updatedCookie += '; ' + optionKey;
                optionValue = options[optionKey];

                if (optionValue !== true) {
                    updatedCookie += '=' + optionValue;
                }
            }

            document.cookie = updatedCookie;
        },

        /**
         * Delete Cookie
         * @param {string} name
         */
        delete: function (name) {
            this.set(name, '', {
                'max-age': -1,
                'path': '/',
                'expires': -1
            });
        }
    };
});

/**
 * Essential Cookie Storage
 */

define('Amasty_GdprFrontendUi/js/storage/essential-cookie',[
    'underscore'
], function (_) {
    'use strict';

    return {
        cookies: [],

        /**
         * Is Essential Cookie
         * @param {string} cookieName
         */
        isEssential: function (cookieName) {
            return this.cookies.indexOf(cookieName) !== -1;
        },

        /**
         * Update Essential Cookie
         * @param {array} groups
         */
        update: function (groups) {
            if (!this.cookies.length) {
                _.each(groups, function (group) {
                    if (group.isEssential) {
                        this.set(group.cookies);
                    }
                }.bind(this));
            }
        },

        set: function (cookies) {
            cookies.forEach(function (item) {
                this.cookies.push(item.name);
            }.bind(this));
        }
    };
});

/**
 * Initialize Google Analytics
 */

define('Amasty_GdprFrontendUi/js/action/ga-initialize',[
    'jquery'
], function ($) {
    'use strict';

    return {
        deferrer: {},

        initialize: function (config) {
            this.deferrer = $.Deferred();

            this.deferrer.done(function () {
                this.run(config);
            }.bind(this));
        },

        run: function (config) {
            (function (i, s, o, g, r, a, m) {
                i.GoogleAnalyticsObject = r;
                i[r] = i[r] || function () {
                    (i[r].q = i[r].q || []).push(arguments);
                }, i[r].l = 1 * new Date();
                a = s.createElement(o),
                m = s.getElementsByTagName(o)[0];
                a.async = 1;
                a.src = g;
                m.parentNode.insertBefore(a, m);
            }(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'));

            // Process page info
            ga('create', config.pageTrackingData.accountId, config.cookieDomain);

            if (config.pageTrackingData.isAnonymizedIpActive) {
                ga('set', 'anonymizeIp', true);
            }

            // Process orders data
            if (config.ordersTrackingData.hasOwnProperty('currency')) {
                ga('require', 'ec', 'ec.js');

                ga('set', 'currencyCode', config.ordersTrackingData.currency);

                // Collect product data for GA
                if (config.ordersTrackingData.products) {
                    $.each(config.ordersTrackingData.products, function (index, value) {
                        ga('ec:addProduct', value);
                    });
                }

                // Collect orders data for GA
                if (config.ordersTrackingData.orders) {
                    $.each(config.ordersTrackingData.orders, function (index, value) {
                        ga('ec:setAction', 'purchase', value);
                    });
                }

                ga('send', 'pageview');
            } else {
                // Process Data if not orders
                ga('send', 'pageview' + config.pageTrackingData.optPageUrl);
            }
        }
    };
});

/**
 * 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);
    };
});

/**
 * Cookie Model
 */

define('Amasty_GdprFrontendUi/js/model/cookie',[
    'jquery',
    'underscore',
    'Amasty_GdprFrontendUi/js/model/cookie-data-provider',
    'Amasty_GdprFrontendUi/js/storage/cookie',
    'Amasty_GdprFrontendUi/js/storage/essential-cookie',
    'Amasty_GdprFrontendUi/js/action/ga-initialize',
    'mage/cookies',
    'jquery/jquery-storageapi'
], function ($, _, cookieDataProvider, cookieStorage, essentialStorage) {
    'use strict';

    return {
        initEventHandlers: function () {
            var body = $('body');

            body.on('amcookie_save', function () {
                this.setLastCookieAcceptance();
            }.bind(this));
            body.on('amcookie_allow', function () {
                this.setLastCookieAcceptance();
            }.bind(this));
        },

        deleteDisallowedCookie: function () {
            var disallowedCookie = $.mage.cookies.get('amcookie_disallowed');

            if (!disallowedCookie) {
                return;
            }

            disallowedCookie.split(',').forEach(function (name) {
                if (!essentialStorage.isEssential(name)) {
                    cookieStorage.delete(name);
                }
            });
        },

        getEssentialGroups: function () {
            var groups,
                filteredGroups;

            cookieDataProvider.getCookieData().done(function (cookieData) {
                groups = cookieData;
            });

            filteredGroups = _.filter(groups, function (group) {
                return group.isEssential;
            });

            return {
                'groups': filteredGroups.map(function (group) {
                    return group.groupId;
                })
            };
        },

        isCookieAllowed: function (cookieName) {
            var allowedGroups = $.mage.cookies.get('amcookie_allowed'),
                disallowedCookie = $.mage.cookies.get('amcookie_disallowed') || '',
                isCookiePolicyAllowed = $.mage.cookies.get('amcookie_policy_restriction') === 'allowed';

            if (!isCookiePolicyAllowed || essentialStorage.isEssential(cookieName)) {
                return true;
            }

            return !((!allowedGroups && !disallowedCookie)
                || disallowedCookie.split(',').indexOf(cookieName) !== -1);
        },

        setLastCookieAcceptance: function () {
            cookieDataProvider.getCookieData().done(function (cookieData) {
                $.localStorage.set('am-last-cookie-acceptance', cookieData.lastUpdate);
            });
        },

        triggerSave: function () {
            $('body').trigger('amcookie_save');
        },

        triggerAllow: function () {
            $('body').trigger('amcookie_allow');
        }
    };
});

define('Amasty_GdprFrontendUi/js/action/cookie-setter',[
    'jquery',
    'Amasty_GdprFrontendUi/js/model/cookie'
], function ($, cookies) {
    'use strict';

    return function (methodSet, parent) {
        return function (cookieName, data) {
            var isCookieAllowed = cookies.isCookieAllowed(cookieName);

            if (isCookieAllowed || !window.isGdprCookieEnabled || cookieName === 'mage-messages') {
                methodSet.call(parent, cookieName, data);
            }
        };
    };
});

define('Amasty_GdprFrontendUi/js/mixins/customer-data-mixin',[
    'jquery',
    'mage/utils/wrapper',
    'Amasty_GdprFrontendUi/js/action/cookie-setter',
    'jquery/jquery-storageapi'
], function ($, wrapper, cookieSetter) {
    'use strict';

    return function (customerData) {
        customerData.init = wrapper.wrapSuper(customerData.init, function () {
            $.cookieStorage.set = cookieSetter($.cookieStorage.set, $.cookieStorage);
            this._super();
        });

        return customerData;
    };
});

( function( factory ) {
	"use strict";

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

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

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

// 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.
return $.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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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" );
            }
        }
    };

} );

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mage/requirejs/resolver',[
    'underscore',
    'domReady!'
], function (_) {
    'use strict';

    var context = require.s.contexts._,
        execCb = context.execCb,
        registry = context.registry,
        callbacks = [],
        retries = 10,
        updateDelay = 1,
        ready,
        update;

    /**
     * Checks if provided callback already exists in the callbacks list.
     *
     * @param {Object} callback - Callback object to be checked.
     * @returns {Boolean}
     */
    function isSubscribed(callback) {
        return !!_.findWhere(callbacks, callback);
    }

    /**
     * Checks if provided module is rejected during load.
     *
     * @param {Object} module - Module to be checked.
     * @return {Boolean}
     */
    function isRejected(module) {
        return registry[module.id] && (registry[module.id].inited || registry[module.id].error);
    }

    /**
     * Checks if provided module had path fallback triggered.
     *
     * @param {Object} module - Module to be checked.
     * @return {Boolean}
     */
    function isPathFallback(module) {
        return registry[module.id] && registry[module.id].events.error;
    }

    /**
     * Checks if provided module has unresolved dependencies.
     *
     * @param {Object} module - Module to be checked.
     * @returns {Boolean}
     */
    function isPending(module) {
        if (!module.depCount) {
            return false;
        }

        return module.depCount >
            _.filter(module.depMaps, isRejected).length + _.filter(module.depMaps, isPathFallback).length;
    }

    /**
     * Checks if requirejs's registry object contains pending modules.
     *
     * @returns {Boolean}
     */
    function hasPending() {
        return _.some(registry, isPending);
    }

    /**
     * Checks if 'resolver' module is in ready
     * state and that there are no pending modules.
     *
     * @returns {Boolean}
     */
    function isReady() {
        return ready && !hasPending();
    }

    /**
     * Invokes provided callback handler.
     *
     * @param {Object} callback
     */
    function invoke(callback) {
        callback.handler.call(callback.ctx);
    }

    /**
     * Sets 'resolver' module to a ready state
     * and invokes pending callbacks.
     */
    function resolve() {
        ready = true;

        callbacks.splice(0).forEach(invoke);
    }

    /**
     * Drops 'ready' flag and runs the update process.
     */
    function tick() {
        ready = false;

        update(retries);
    }

    /**
     * Adds callback which will be invoked
     * when all of the pending modules are initiated.
     *
     * @param {Function} handler - 'Ready' event handler function.
     * @param {Object} [ctx] - Optional context with which handler
     *      will be invoked.
     */
    function subscribe(handler, ctx) {
        var callback = {
            handler: handler,
            ctx: ctx
        };

        if (!isSubscribed(callback)) {
            callbacks.push(callback);

            if (isReady()) {
                _.defer(tick);
            }
        }
    }

    /**
     * Checks for all modules to be initiated
     * and invokes pending callbacks if it's so.
     *
     * @param {Number} [retry] - Number of retries
     *      that will be used to repeat the 'update' function
     *      invokation in case if there are no pending requests.
     */
    update = _.debounce(function (retry) {
        if (!hasPending()) {
            retry ? update(--retry) : resolve();
        }
    }, updateDelay);

    /**
     * Overrides requirejs's original 'execCb' method
     * in order to track pending modules.
     *
     * @returns {*} Result of original method call.
     */
    context.execCb = function () {
        var exported = execCb.apply(context, arguments);

        tick();

        return exported;
    };

    return subscribe;
});

/**
 * 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/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;
});

/*!
 * 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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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
( function( factory ) {
    "use strict";

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

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

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

    return $.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/

( function( factory ) {
    "use strict";

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

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

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

// 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 );
        }
    } );

    return $.ui.focusable;

} );

( function( factory ) {
	"use strict";

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

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

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

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

/*!
 * 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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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 Support for jQuery core 1.8.x and newer 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 */

//>>label: jQuery 1.8+ Support
//>>group: Core
//>>description: Support version 1.8.x and newer of jQuery core

( function( factory ) {
    "use strict";

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

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

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

// Support: jQuery 1.9.x or older
// $.expr[ ":" ] is deprecated.
    if ( !$.expr.pseudos ) {
        $.expr.pseudos = $.expr[ ":" ];
    }

// Support: jQuery 1.11.x or older
// $.unique has been renamed to $.uniqueSort
    if ( !$.uniqueSort ) {
        $.uniqueSort = $.unique;
    }

// Support: jQuery 2.2.x or older.
// This method has been defined in jQuery 3.0.0.
// Code from https://github.com/jquery/jquery/blob/e539bac79e666bba95bba86d690b4e609dca2286/src/selector/escapeSelector.js
    if ( !$.escapeSelector ) {

        // CSS string/identifier serialization
        // https://drafts.csswg.org/cssom/#common-serializing-idioms
        var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;

        var fcssescape = function( ch, asCodePoint ) {
            if ( asCodePoint ) {

                // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
                if ( ch === "\0" ) {
                    return "\uFFFD";
                }

                // Control characters and (dependent upon position) numbers get escaped as code points
                return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
            }

            // Other potentially-special ASCII characters get backslash-escaped
            return "\\" + ch;
        };

        $.escapeSelector = function( sel ) {
            return ( sel + "" ).replace( rcssescape, fcssescape );
        };
    }

// Support: jQuery 3.4.x or older
// These methods have been defined in jQuery 3.5.0.
    if ( !$.fn.even || !$.fn.odd ) {
        $.fn.extend( {
            even: function() {
                return this.filter( function( i ) {
                    return i % 2 === 0;
                } );
            },
            odd: function() {
                return this.filter( function( i ) {
                    return i % 2 === 1;
                } );
            }
        } );
    }

} );

( function( factory ) {
	"use strict";

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

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

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

// $.ui.plugin is deprecated. Use $.widget() extensions instead.
return $.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 );
			}
		}
	}
};

} );

( function( factory ) {
	"use strict";

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

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

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

return $.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 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/

( function( factory ) {
    "use strict";

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

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

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

    return $.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/

( function( factory ) {
    "use strict";

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

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

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

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

} );

// This file is deprecated in 1.12.0 to be removed in 1.14
( function() {
"use strict";

define('jquery/ui-modules/core', [
	"jquery",
	"./data",
	"./disable-selection",
	"./focusable",
	"./form",
	"./ie",
	"./keycode",
	"./labels",
	"./jquery-patch",
	"./plugin",
	"./safe-active-element",
	"./safe-blur",
	"./scroll-parent",
	"./tabbable",
	"./unique-id",
	"./version"
] );
} )();

/*!
 * 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( factory ) {
    "use strict";

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

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

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

    ( 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 );
                }
            }
        };

    } )();

    return $.ui.position;

} );

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

//>>label: Tooltip
//>>group: Widgets
//>>description: Shows additional information for any element on hover or focus.
//>>docs: http://api.jqueryui.com/tooltip/
//>>demos: http://jqueryui.com/tooltip/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/tooltip.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/tooltip',[
            "jquery",
            "../keycode",
            "../position",
            "../unique-id",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.widget( "ui.tooltip", {
        version: "1.13.2",
        options: {
            classes: {
                "ui-tooltip": "ui-corner-all ui-widget-shadow"
            },
            content: function() {
                var title = $( this ).attr( "title" );

                // Escape title, since we're going from an attribute to raw HTML
                return $( "<a>" ).text( title ).html();
            },
            hide: true,

            // Disabled elements have inconsistent behavior across browsers (#8661)
            items: "[title]:not([disabled])",
            position: {
                my: "left top+15",
                at: "left bottom",
                collision: "flipfit flip"
            },
            show: true,
            track: false,

            // Callbacks
            close: null,
            open: null
        },

        _addDescribedBy: function( elem, id ) {
            var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
            describedby.push( id );
            elem
                .data( "ui-tooltip-id", id )
                .attr( "aria-describedby", String.prototype.trim.call( describedby.join( " " ) ) );
        },

        _removeDescribedBy: function( elem ) {
            var id = elem.data( "ui-tooltip-id" ),
                describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
                index = $.inArray( id, describedby );

            if ( index !== -1 ) {
                describedby.splice( index, 1 );
            }

            elem.removeData( "ui-tooltip-id" );
            describedby = String.prototype.trim.call( describedby.join( " " ) );
            if ( describedby ) {
                elem.attr( "aria-describedby", describedby );
            } else {
                elem.removeAttr( "aria-describedby" );
            }
        },

        _create: function() {
            this._on( {
                mouseover: "open",
                focusin: "open"
            } );

            // IDs of generated tooltips, needed for destroy
            this.tooltips = {};

            // IDs of parent tooltips where we removed the title attribute
            this.parents = {};

            // Append the aria-live region so tooltips announce correctly
            this.liveRegion = $( "<div>" )
                .attr( {
                    role: "log",
                    "aria-live": "assertive",
                    "aria-relevant": "additions"
                } )
                .appendTo( this.document[ 0 ].body );
            this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );

            this.disabledTitles = $( [] );
        },

        _setOption: function( key, value ) {
            var that = this;

            this._super( key, value );

            if ( key === "content" ) {
                $.each( this.tooltips, function( id, tooltipData ) {
                    that._updateContent( tooltipData.element );
                } );
            }
        },

        _setOptionDisabled: function( value ) {
            this[ value ? "_disable" : "_enable" ]();
        },

        _disable: function() {
            var that = this;

            // Close open tooltips
            $.each( this.tooltips, function( id, tooltipData ) {
                var event = $.Event( "blur" );
                event.target = event.currentTarget = tooltipData.element[ 0 ];
                that.close( event, true );
            } );

            // Remove title attributes to prevent native tooltips
            this.disabledTitles = this.disabledTitles.add(
                this.element.find( this.options.items ).addBack()
                    .filter( function() {
                        var element = $( this );
                        if ( element.is( "[title]" ) ) {
                            return element
                                .data( "ui-tooltip-title", element.attr( "title" ) )
                                .removeAttr( "title" );
                        }
                    } )
            );
        },

        _enable: function() {

            // restore title attributes
            this.disabledTitles.each( function() {
                var element = $( this );
                if ( element.data( "ui-tooltip-title" ) ) {
                    element.attr( "title", element.data( "ui-tooltip-title" ) );
                }
            } );
            this.disabledTitles = $( [] );
        },

        open: function( event ) {
            var that = this,
                target = $( event ? event.target : this.element )

                    // we need closest here due to mouseover bubbling,
                    // but always pointing at the same event target
                    .closest( this.options.items );

            // No element to show a tooltip for or the tooltip is already open
            if ( !target.length || target.data( "ui-tooltip-id" ) ) {
                return;
            }

            if ( target.attr( "title" ) ) {
                target.data( "ui-tooltip-title", target.attr( "title" ) );
            }

            target.data( "ui-tooltip-open", true );

            // Kill parent tooltips, custom or native, for hover
            if ( event && event.type === "mouseover" ) {
                target.parents().each( function() {
                    var parent = $( this ),
                        blurEvent;
                    if ( parent.data( "ui-tooltip-open" ) ) {
                        blurEvent = $.Event( "blur" );
                        blurEvent.target = blurEvent.currentTarget = this;
                        that.close( blurEvent, true );
                    }
                    if ( parent.attr( "title" ) ) {
                        parent.uniqueId();
                        that.parents[ this.id ] = {
                            element: this,
                            title: parent.attr( "title" )
                        };
                        parent.attr( "title", "" );
                    }
                } );
            }

            this._registerCloseHandlers( event, target );
            this._updateContent( target, event );
        },

        _updateContent: function( target, event ) {
            var content,
                contentOption = this.options.content,
                that = this,
                eventType = event ? event.type : null;

            if ( typeof contentOption === "string" || contentOption.nodeType ||
                contentOption.jquery ) {
                return this._open( event, target, contentOption );
            }

            content = contentOption.call( target[ 0 ], function( response ) {

                // IE may instantly serve a cached response for ajax requests
                // delay this call to _open so the other call to _open runs first
                that._delay( function() {

                    // Ignore async response if tooltip was closed already
                    if ( !target.data( "ui-tooltip-open" ) ) {
                        return;
                    }

                    // JQuery creates a special event for focusin when it doesn't
                    // exist natively. To improve performance, the native event
                    // object is reused and the type is changed. Therefore, we can't
                    // rely on the type being correct after the event finished
                    // bubbling, so we set it back to the previous value. (#8740)
                    if ( event ) {
                        event.type = eventType;
                    }
                    this._open( event, target, response );
                } );
            } );
            if ( content ) {
                this._open( event, target, content );
            }
        },

        _open: function( event, target, content ) {
            var tooltipData, tooltip, delayedShow, a11yContent,
                positionOption = $.extend( {}, this.options.position );

            if ( !content ) {
                return;
            }

            // Content can be updated multiple times. If the tooltip already
            // exists, then just update the content and bail.
            tooltipData = this._find( target );
            if ( tooltipData ) {
                tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
                return;
            }

            // If we have a title, clear it to prevent the native tooltip
            // we have to check first to avoid defining a title if none exists
            // (we don't want to cause an element to start matching [title])
            //
            // We use removeAttr only for key events, to allow IE to export the correct
            // accessible attributes. For mouse events, set to empty string to avoid
            // native tooltip showing up (happens only when removing inside mouseover).
            if ( target.is( "[title]" ) ) {
                if ( event && event.type === "mouseover" ) {
                    target.attr( "title", "" );
                } else {
                    target.removeAttr( "title" );
                }
            }

            tooltipData = this._tooltip( target );
            tooltip = tooltipData.tooltip;
            this._addDescribedBy( target, tooltip.attr( "id" ) );
            tooltip.find( ".ui-tooltip-content" ).html( content );

            // Support: Voiceover on OS X, JAWS on IE <= 9
            // JAWS announces deletions even when aria-relevant="additions"
            // Voiceover will sometimes re-read the entire log region's contents from the beginning
            this.liveRegion.children().hide();
            a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
            a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
            a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
            a11yContent.appendTo( this.liveRegion );

            function position( event ) {
                positionOption.of = event;
                if ( tooltip.is( ":hidden" ) ) {
                    return;
                }
                tooltip.position( positionOption );
            }
            if ( this.options.track && event && /^mouse/.test( event.type ) ) {
                this._on( this.document, {
                    mousemove: position
                } );

                // trigger once to override element-relative positioning
                position( event );
            } else {
                tooltip.position( $.extend( {
                    of: target
                }, this.options.position ) );
            }

            tooltip.hide();

            this._show( tooltip, this.options.show );

            // Handle tracking tooltips that are shown with a delay (#8644). As soon
            // as the tooltip is visible, position the tooltip using the most recent
            // event.
            // Adds the check to add the timers only when both delay and track options are set (#14682)
            if ( this.options.track && this.options.show && this.options.show.delay ) {
                delayedShow = this.delayedShow = setInterval( function() {
                    if ( tooltip.is( ":visible" ) ) {
                        position( positionOption.of );
                        clearInterval( delayedShow );
                    }
                }, 13 );
            }

            this._trigger( "open", event, { tooltip: tooltip } );
        },

        _registerCloseHandlers: function( event, target ) {
            var events = {
                keyup: function( event ) {
                    if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
                        var fakeEvent = $.Event( event );
                        fakeEvent.currentTarget = target[ 0 ];
                        this.close( fakeEvent, true );
                    }
                }
            };

            // Only bind remove handler for delegated targets. Non-delegated
            // tooltips will handle this in destroy.
            if ( target[ 0 ] !== this.element[ 0 ] ) {
                events.remove = function() {
                    var targetElement = this._find( target );
                    if ( targetElement ) {
                        this._removeTooltip( targetElement.tooltip );
                    }
                };
            }

            if ( !event || event.type === "mouseover" ) {
                events.mouseleave = "close";
            }
            if ( !event || event.type === "focusin" ) {
                events.focusout = "close";
            }
            this._on( true, target, events );
        },

        close: function( event ) {
            var tooltip,
                that = this,
                target = $( event ? event.currentTarget : this.element ),
                tooltipData = this._find( target );

            // The tooltip may already be closed
            if ( !tooltipData ) {

                // We set ui-tooltip-open immediately upon open (in open()), but only set the
                // additional data once there's actually content to show (in _open()). So even if the
                // tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
                // the period between open() and _open().
                target.removeData( "ui-tooltip-open" );
                return;
            }

            tooltip = tooltipData.tooltip;

            // Disabling closes the tooltip, so we need to track when we're closing
            // to avoid an infinite loop in case the tooltip becomes disabled on close
            if ( tooltipData.closing ) {
                return;
            }

            // Clear the interval for delayed tracking tooltips
            clearInterval( this.delayedShow );

            // Only set title if we had one before (see comment in _open())
            // If the title attribute has changed since open(), don't restore
            if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
                target.attr( "title", target.data( "ui-tooltip-title" ) );
            }

            this._removeDescribedBy( target );

            tooltipData.hiding = true;
            tooltip.stop( true );
            this._hide( tooltip, this.options.hide, function() {
                that._removeTooltip( $( this ) );
            } );

            target.removeData( "ui-tooltip-open" );
            this._off( target, "mouseleave focusout keyup" );

            // Remove 'remove' binding only on delegated targets
            if ( target[ 0 ] !== this.element[ 0 ] ) {
                this._off( target, "remove" );
            }
            this._off( this.document, "mousemove" );

            if ( event && event.type === "mouseleave" ) {
                $.each( this.parents, function( id, parent ) {
                    $( parent.element ).attr( "title", parent.title );
                    delete that.parents[ id ];
                } );
            }

            tooltipData.closing = true;
            this._trigger( "close", event, { tooltip: tooltip } );
            if ( !tooltipData.hiding ) {
                tooltipData.closing = false;
            }
        },

        _tooltip: function( element ) {
            var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
                content = $( "<div>" ).appendTo( tooltip ),
                id = tooltip.uniqueId().attr( "id" );

            this._addClass( content, "ui-tooltip-content" );
            this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );

            tooltip.appendTo( this._appendTo( element ) );

            return this.tooltips[ id ] = {
                element: element,
                tooltip: tooltip
            };
        },

        _find: function( target ) {
            var id = target.data( "ui-tooltip-id" );
            return id ? this.tooltips[ id ] : null;
        },

        _removeTooltip: function( tooltip ) {

            // Clear the interval for delayed tracking tooltips
            clearInterval( this.delayedShow );

            tooltip.remove();
            delete this.tooltips[ tooltip.attr( "id" ) ];
        },

        _appendTo: function( target ) {
            var element = target.closest( ".ui-front, dialog" );

            if ( !element.length ) {
                element = this.document[ 0 ].body;
            }

            return element;
        },

        _destroy: function() {
            var that = this;

            // Close open tooltips
            $.each( this.tooltips, function( id, tooltipData ) {

                // Delegate to close method to handle common cleanup
                var event = $.Event( "blur" ),
                    element = tooltipData.element;
                event.target = event.currentTarget = element[ 0 ];
                that.close( event, true );

                // Remove immediately; destroying an open tooltip doesn't use the
                // hide animation
                $( "#" + id ).remove();

                // Restore the title
                if ( element.data( "ui-tooltip-title" ) ) {

                    // If the title attribute has changed since open(), don't restore
                    if ( !element.attr( "title" ) ) {
                        element.attr( "title", element.data( "ui-tooltip-title" ) );
                    }
                    element.removeData( "ui-tooltip-title" );
                }
            } );
            this.liveRegion.remove();
        }
    } );

// DEPRECATED
// TODO: Switch return back to widget declaration at top of file when this is removed
    if ( $.uiBackCompat !== false ) {

        // Backcompat for tooltipClass option
        $.widget( "ui.tooltip", $.ui.tooltip, {
            options: {
                tooltipClass: null
            },
            _tooltip: function() {
                var tooltipData = this._superApply( arguments );
                if ( this.options.tooltipClass ) {
                    tooltipData.tooltip.addClass( this.options.tooltipClass );
                }
                return tooltipData;
            }
        } );
    }

    return $.ui.tooltip;

} );

/**
 * 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_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_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_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');
});

/*!
 * 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/

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/mouse',[
            "jquery",
            "../ie",
            "../version",
            "../widget"
        ], factory );
    } else {

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

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

    return $.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;
        }
    } );

} );

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

//>>label: Sortable
//>>group: Interactions
//>>description: Enables items in a list to be sorted using the mouse.
//>>docs: http://api.jqueryui.com/sortable/
//>>demos: http://jqueryui.com/sortable/
//>>css.structure: ../../themes/base/sortable.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/sortable',[
            "jquery",
            "./mouse",
            "../data",
            "../ie",
            "../scroll-parent",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    return $.widget( "ui.sortable", $.ui.mouse, {
        version: "1.13.2",
        widgetEventPrefix: "sort",
        ready: false,
        options: {
            appendTo: "parent",
            axis: false,
            connectWith: false,
            containment: false,
            cursor: "auto",
            cursorAt: false,
            dropOnEmpty: true,
            forcePlaceholderSize: false,
            forceHelperSize: false,
            grid: false,
            handle: false,
            helper: "original",
            items: "> *",
            opacity: false,
            placeholder: false,
            revert: false,
            scroll: true,
            scrollSensitivity: 20,
            scrollSpeed: 20,
            scope: "default",
            tolerance: "intersect",
            zIndex: 1000,

            // Callbacks
            activate: null,
            beforeStop: null,
            change: null,
            deactivate: null,
            out: null,
            over: null,
            receive: null,
            remove: null,
            sort: null,
            start: null,
            stop: null,
            update: null
        },

        _isOverAxis: function( x, reference, size ) {
            return ( x >= reference ) && ( x < ( reference + size ) );
        },

        _isFloating: function( item ) {
            return ( /left|right/ ).test( item.css( "float" ) ) ||
                ( /inline|table-cell/ ).test( item.css( "display" ) );
        },

        _create: function() {
            this.containerCache = {};
            this._addClass( "ui-sortable" );

            //Get the items
            this.refresh();

            //Let's determine the parent's offset
            this.offset = this.element.offset();

            //Initialize mouse events for interaction
            this._mouseInit();

            this._setHandleClassName();

            //We're ready to go
            this.ready = true;

        },

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

            if ( key === "handle" ) {
                this._setHandleClassName();
            }
        },

        _setHandleClassName: function() {
            var that = this;
            this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
            $.each( this.items, function() {
                that._addClass(
                    this.instance.options.handle ?
                        this.item.find( this.instance.options.handle ) :
                        this.item,
                    "ui-sortable-handle"
                );
            } );
        },

        _destroy: function() {
            this._mouseDestroy();

            for ( var i = this.items.length - 1; i >= 0; i-- ) {
                this.items[ i ].item.removeData( this.widgetName + "-item" );
            }

            return this;
        },

        _mouseCapture: function( event, overrideHandle ) {
            var currentItem = null,
                validHandle = false,
                that = this;

            if ( this.reverting ) {
                return false;
            }

            if ( this.options.disabled || this.options.type === "static" ) {
                return false;
            }

            //We have to refresh the items data once first
            this._refreshItems( event );

            //Find out if the clicked node (or one of its parents) is a actual item in this.items
            $( event.target ).parents().each( function() {
                if ( $.data( this, that.widgetName + "-item" ) === that ) {
                    currentItem = $( this );
                    return false;
                }
            } );
            if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
                currentItem = $( event.target );
            }

            if ( !currentItem ) {
                return false;
            }
            if ( this.options.handle && !overrideHandle ) {
                $( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
                    if ( this === event.target ) {
                        validHandle = true;
                    }
                } );
                if ( !validHandle ) {
                    return false;
                }
            }

            this.currentItem = currentItem;
            this._removeCurrentsFromItems();
            return true;

        },

        _mouseStart: function( event, overrideHandle, noActivation ) {

            var i, body,
                o = this.options;

            this.currentContainer = this;

            //We only need to call refreshPositions, because the refreshItems call has been moved to
            // mouseCapture
            this.refreshPositions();

            //Prepare the dragged items parent
            this.appendTo = $( o.appendTo !== "parent" ?
                o.appendTo :
                this.currentItem.parent() );

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

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

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

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

            //The element's absolute position on the page minus margins
            this.offset = this.currentItem.offset();
            this.offset = {
                top: this.offset.top - this.margins.top,
                left: this.offset.left - this.margins.left
            };

            $.extend( this.offset, {
                click: { //Where the click happened, relative to the element
                    left: event.pageX - this.offset.left,
                    top: event.pageY - this.offset.top
                },

                // This is a relative to absolute position minus the actual position calculation -
                // only used for relative positioned helper
                relative: this._getRelativeOffset()
            } );

            // After we get the helper offset, but before we get the parent offset we can
            // change the helper's position to absolute
            // TODO: Still need to figure out a way to make relative sorting possible
            this.helper.css( "position", "absolute" );
            this.cssPosition = this.helper.css( "position" );

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

            //Cache the former DOM position
            this.domPosition = {
                prev: this.currentItem.prev()[ 0 ],
                parent: this.currentItem.parent()[ 0 ]
            };

            // If the helper is not the original, hide the original so it's not playing any role during
            // the drag, won't cause anything bad this way
            if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
                this.currentItem.hide();
            }

            //Create the placeholder
            this._createPlaceholder();

            //Get the next scrolling parent
            this.scrollParent = this.placeholder.scrollParent();

            $.extend( this.offset, {
                parent: this._getParentOffset()
            } );

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

            if ( o.cursor && o.cursor !== "auto" ) { // cursor option
                body = this.document.find( "body" );

                // Support: IE
                this.storedCursor = body.css( "cursor" );
                body.css( "cursor", o.cursor );

                this.storedStylesheet =
                    $( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
            }

            // We need to make sure to grab the zIndex before setting the
            // opacity, because setting the opacity to anything lower than 1
            // causes the zIndex to change from "auto" to 0.
            if ( o.zIndex ) { // zIndex option
                if ( this.helper.css( "zIndex" ) ) {
                    this._storedZIndex = this.helper.css( "zIndex" );
                }
                this.helper.css( "zIndex", o.zIndex );
            }

            if ( o.opacity ) { // opacity option
                if ( this.helper.css( "opacity" ) ) {
                    this._storedOpacity = this.helper.css( "opacity" );
                }
                this.helper.css( "opacity", o.opacity );
            }

            //Prepare scrolling
            if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                this.scrollParent[ 0 ].tagName !== "HTML" ) {
                this.overflowOffset = this.scrollParent.offset();
            }

            //Call callbacks
            this._trigger( "start", event, this._uiHash() );

            //Recache the helper size
            if ( !this._preserveHelperProportions ) {
                this._cacheHelperProportions();
            }

            //Post "activate" events to possible containers
            if ( !noActivation ) {
                for ( i = this.containers.length - 1; i >= 0; i-- ) {
                    this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
                }
            }

            //Prepare possible droppables
            if ( $.ui.ddmanager ) {
                $.ui.ddmanager.current = this;
            }

            if ( $.ui.ddmanager && !o.dropBehaviour ) {
                $.ui.ddmanager.prepareOffsets( this, event );
            }

            this.dragging = true;

            this._addClass( this.helper, "ui-sortable-helper" );

            //Move the helper, if needed
            if ( !this.helper.parent().is( this.appendTo ) ) {
                this.helper.detach().appendTo( this.appendTo );

                //Update position
                this.offset.parent = this._getParentOffset();
            }

            //Generate the original position
            this.position = this.originalPosition = this._generatePosition( event );
            this.originalPageX = event.pageX;
            this.originalPageY = event.pageY;
            this.lastPositionAbs = this.positionAbs = this._convertPositionTo( "absolute" );

            this._mouseDrag( event );

            return true;

        },

        _scroll: function( event ) {
            var o = this.options,
                scrolled = false;

            if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                this.scrollParent[ 0 ].tagName !== "HTML" ) {

                if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
                    event.pageY < o.scrollSensitivity ) {
                    this.scrollParent[ 0 ].scrollTop =
                        scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
                } else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
                    this.scrollParent[ 0 ].scrollTop =
                        scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
                }

                if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
                    event.pageX < o.scrollSensitivity ) {
                    this.scrollParent[ 0 ].scrollLeft = scrolled =
                        this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
                } else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
                    this.scrollParent[ 0 ].scrollLeft = scrolled =
                        this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
                }

            } else {

                if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
                    scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
                } else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
                    o.scrollSensitivity ) {
                    scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
                }

                if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
                    scrolled = this.document.scrollLeft(
                        this.document.scrollLeft() - o.scrollSpeed
                    );
                } else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
                    o.scrollSensitivity ) {
                    scrolled = this.document.scrollLeft(
                        this.document.scrollLeft() + o.scrollSpeed
                    );
                }

            }

            return scrolled;
        },

        _mouseDrag: function( event ) {
            var i, item, itemElement, intersection,
                o = this.options;

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

            //Set the helper position
            if ( !this.options.axis || this.options.axis !== "y" ) {
                this.helper[ 0 ].style.left = this.position.left + "px";
            }
            if ( !this.options.axis || this.options.axis !== "x" ) {
                this.helper[ 0 ].style.top = this.position.top + "px";
            }

            //Do scrolling
            if ( o.scroll ) {
                if ( this._scroll( event ) !== false ) {

                    //Update item positions used in position checks
                    this._refreshItemPositions( true );

                    if ( $.ui.ddmanager && !o.dropBehaviour ) {
                        $.ui.ddmanager.prepareOffsets( this, event );
                    }
                }
            }

            this.dragDirection = {
                vertical: this._getDragVerticalDirection(),
                horizontal: this._getDragHorizontalDirection()
            };

            //Rearrange
            for ( i = this.items.length - 1; i >= 0; i-- ) {

                //Cache variables and intersection, continue if no intersection
                item = this.items[ i ];
                itemElement = item.item[ 0 ];
                intersection = this._intersectsWithPointer( item );
                if ( !intersection ) {
                    continue;
                }

                // Only put the placeholder inside the current Container, skip all
                // items from other containers. This works because when moving
                // an item from one container to another the
                // currentContainer is switched before the placeholder is moved.
                //
                // Without this, moving items in "sub-sortables" can cause
                // the placeholder to jitter between the outer and inner container.
                if ( item.instance !== this.currentContainer ) {
                    continue;
                }

                // Cannot intersect with itself
                // no useless actions that have been done before
                // no action if the item moved is the parent of the item checked
                if ( itemElement !== this.currentItem[ 0 ] &&
                    this.placeholder[ intersection === 1 ?
                        "next" : "prev" ]()[ 0 ] !== itemElement &&
                    !$.contains( this.placeholder[ 0 ], itemElement ) &&
                    ( this.options.type === "semi-dynamic" ?
                            !$.contains( this.element[ 0 ], itemElement ) :
                            true
                    )
                ) {

                    this.direction = intersection === 1 ? "down" : "up";

                    if ( this.options.tolerance === "pointer" ||
                        this._intersectsWithSides( item ) ) {
                        this._rearrange( event, item );
                    } else {
                        break;
                    }

                    this._trigger( "change", event, this._uiHash() );
                    break;
                }
            }

            //Post events to containers
            this._contactContainers( event );

            //Interconnect with droppables
            if ( $.ui.ddmanager ) {
                $.ui.ddmanager.drag( this, event );
            }

            //Call callbacks
            this._trigger( "sort", event, this._uiHash() );

            this.lastPositionAbs = this.positionAbs;
            return false;

        },

        _mouseStop: function( event, noPropagation ) {

            if ( !event ) {
                return;
            }

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

            if ( this.options.revert ) {
                var that = this,
                    cur = this.placeholder.offset(),
                    axis = this.options.axis,
                    animation = {};

                if ( !axis || axis === "x" ) {
                    animation.left = cur.left - this.offset.parent.left - this.margins.left +
                        ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
                                0 :
                                this.offsetParent[ 0 ].scrollLeft
                        );
                }
                if ( !axis || axis === "y" ) {
                    animation.top = cur.top - this.offset.parent.top - this.margins.top +
                        ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
                                0 :
                                this.offsetParent[ 0 ].scrollTop
                        );
                }
                this.reverting = true;
                $( this.helper ).animate(
                    animation,
                    parseInt( this.options.revert, 10 ) || 500,
                    function() {
                        that._clear( event );
                    }
                );
            } else {
                this._clear( event, noPropagation );
            }

            return false;

        },

        cancel: function() {

            if ( this.dragging ) {

                this._mouseUp( new $.Event( "mouseup", { target: null } ) );

                if ( this.options.helper === "original" ) {
                    this.currentItem.css( this._storedCSS );
                    this._removeClass( this.currentItem, "ui-sortable-helper" );
                } else {
                    this.currentItem.show();
                }

                //Post deactivating events to containers
                for ( var i = this.containers.length - 1; i >= 0; i-- ) {
                    this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
                    if ( this.containers[ i ].containerCache.over ) {
                        this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
                        this.containers[ i ].containerCache.over = 0;
                    }
                }

            }

            if ( this.placeholder ) {

                //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
                // it unbinds ALL events from the original node!
                if ( this.placeholder[ 0 ].parentNode ) {
                    this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
                }
                if ( this.options.helper !== "original" && this.helper &&
                    this.helper[ 0 ].parentNode ) {
                    this.helper.remove();
                }

                $.extend( this, {
                    helper: null,
                    dragging: false,
                    reverting: false,
                    _noFinalSort: null
                } );

                if ( this.domPosition.prev ) {
                    $( this.domPosition.prev ).after( this.currentItem );
                } else {
                    $( this.domPosition.parent ).prepend( this.currentItem );
                }
            }

            return this;

        },

        serialize: function( o ) {

            var items = this._getItemsAsjQuery( o && o.connected ),
                str = [];
            o = o || {};

            $( items ).each( function() {
                var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
                    .match( o.expression || ( /(.+)[\-=_](.+)/ ) );
                if ( res ) {
                    str.push(
                        ( o.key || res[ 1 ] + "[]" ) +
                        "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
                }
            } );

            if ( !str.length && o.key ) {
                str.push( o.key + "=" );
            }

            return str.join( "&" );

        },

        toArray: function( o ) {

            var items = this._getItemsAsjQuery( o && o.connected ),
                ret = [];

            o = o || {};

            items.each( function() {
                ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
            } );
            return ret;

        },

        /* Be careful with the following core functions */
        _intersectsWith: function( item ) {

            var x1 = this.positionAbs.left,
                x2 = x1 + this.helperProportions.width,
                y1 = this.positionAbs.top,
                y2 = y1 + this.helperProportions.height,
                l = item.left,
                r = l + item.width,
                t = item.top,
                b = t + item.height,
                dyClick = this.offset.click.top,
                dxClick = this.offset.click.left,
                isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
                    ( y1 + dyClick ) < b ),
                isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
                    ( x1 + dxClick ) < r ),
                isOverElement = isOverElementHeight && isOverElementWidth;

            if ( this.options.tolerance === "pointer" ||
                this.options.forcePointerForContainers ||
                ( this.options.tolerance !== "pointer" &&
                    this.helperProportions[ this.floating ? "width" : "height" ] >
                    item[ this.floating ? "width" : "height" ] )
            ) {
                return isOverElement;
            } else {

                return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
                    x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
                    t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
                    y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half

            }
        },

        _intersectsWithPointer: function( item ) {
            var verticalDirection, horizontalDirection,
                isOverElementHeight = ( this.options.axis === "x" ) ||
                    this._isOverAxis(
                        this.positionAbs.top + this.offset.click.top, item.top, item.height ),
                isOverElementWidth = ( this.options.axis === "y" ) ||
                    this._isOverAxis(
                        this.positionAbs.left + this.offset.click.left, item.left, item.width ),
                isOverElement = isOverElementHeight && isOverElementWidth;

            if ( !isOverElement ) {
                return false;
            }

            verticalDirection = this.dragDirection.vertical;
            horizontalDirection = this.dragDirection.horizontal;

            return this.floating ?
                ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 ) :
                ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );

        },

        _intersectsWithSides: function( item ) {

            var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
                    this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
                isOverRightHalf = this._isOverAxis( this.positionAbs.left +
                    this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
                verticalDirection = this.dragDirection.vertical,
                horizontalDirection = this.dragDirection.horizontal;

            if ( this.floating && horizontalDirection ) {
                return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
                    ( horizontalDirection === "left" && !isOverRightHalf ) );
            } else {
                return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
                    ( verticalDirection === "up" && !isOverBottomHalf ) );
            }

        },

        _getDragVerticalDirection: function() {
            var delta = this.positionAbs.top - this.lastPositionAbs.top;
            return delta !== 0 && ( delta > 0 ? "down" : "up" );
        },

        _getDragHorizontalDirection: function() {
            var delta = this.positionAbs.left - this.lastPositionAbs.left;
            return delta !== 0 && ( delta > 0 ? "right" : "left" );
        },

        refresh: function( event ) {
            this._refreshItems( event );
            this._setHandleClassName();
            this.refreshPositions();
            return this;
        },

        _connectWith: function() {
            var options = this.options;
            return options.connectWith.constructor === String ?
                [ options.connectWith ] :
                options.connectWith;
        },

        _getItemsAsjQuery: function( connected ) {

            var i, j, cur, inst,
                items = [],
                queries = [],
                connectWith = this._connectWith();

            if ( connectWith && connected ) {
                for ( i = connectWith.length - 1; i >= 0; i-- ) {
                    cur = $( connectWith[ i ], this.document[ 0 ] );
                    for ( j = cur.length - 1; j >= 0; j-- ) {
                        inst = $.data( cur[ j ], this.widgetFullName );
                        if ( inst && inst !== this && !inst.options.disabled ) {
                            queries.push( [ typeof inst.options.items === "function" ?
                                inst.options.items.call( inst.element ) :
                                $( inst.options.items, inst.element )
                                    .not( ".ui-sortable-helper" )
                                    .not( ".ui-sortable-placeholder" ), inst ] );
                        }
                    }
                }
            }

            queries.push( [ typeof this.options.items === "function" ?
                this.options.items
                    .call( this.element, null, { options: this.options, item: this.currentItem } ) :
                $( this.options.items, this.element )
                    .not( ".ui-sortable-helper" )
                    .not( ".ui-sortable-placeholder" ), this ] );

            function addItems() {
                items.push( this );
            }
            for ( i = queries.length - 1; i >= 0; i-- ) {
                queries[ i ][ 0 ].each( addItems );
            }

            return $( items );

        },

        _removeCurrentsFromItems: function() {

            var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );

            this.items = $.grep( this.items, function( item ) {
                for ( var j = 0; j < list.length; j++ ) {
                    if ( list[ j ] === item.item[ 0 ] ) {
                        return false;
                    }
                }
                return true;
            } );

        },

        _refreshItems: function( event ) {

            this.items = [];
            this.containers = [ this ];

            var i, j, cur, inst, targetData, _queries, item, queriesLength,
                items = this.items,
                queries = [ [ typeof this.options.items === "function" ?
                    this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
                    $( this.options.items, this.element ), this ] ],
                connectWith = this._connectWith();

            //Shouldn't be run the first time through due to massive slow-down
            if ( connectWith && this.ready ) {
                for ( i = connectWith.length - 1; i >= 0; i-- ) {
                    cur = $( connectWith[ i ], this.document[ 0 ] );
                    for ( j = cur.length - 1; j >= 0; j-- ) {
                        inst = $.data( cur[ j ], this.widgetFullName );
                        if ( inst && inst !== this && !inst.options.disabled ) {
                            queries.push( [ typeof inst.options.items === "function" ?
                                inst.options.items
                                    .call( inst.element[ 0 ], event, { item: this.currentItem } ) :
                                $( inst.options.items, inst.element ), inst ] );
                            this.containers.push( inst );
                        }
                    }
                }
            }

            for ( i = queries.length - 1; i >= 0; i-- ) {
                targetData = queries[ i ][ 1 ];
                _queries = queries[ i ][ 0 ];

                for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
                    item = $( _queries[ j ] );

                    // Data for target checking (mouse manager)
                    item.data( this.widgetName + "-item", targetData );

                    items.push( {
                        item: item,
                        instance: targetData,
                        width: 0, height: 0,
                        left: 0, top: 0
                    } );
                }
            }

        },

        _refreshItemPositions: function( fast ) {
            var i, item, t, p;

            for ( i = this.items.length - 1; i >= 0; i-- ) {
                item = this.items[ i ];

                //We ignore calculating positions of all connected containers when we're not over them
                if ( this.currentContainer && item.instance !== this.currentContainer &&
                    item.item[ 0 ] !== this.currentItem[ 0 ] ) {
                    continue;
                }

                t = this.options.toleranceElement ?
                    $( this.options.toleranceElement, item.item ) :
                    item.item;

                if ( !fast ) {
                    item.width = t.outerWidth();
                    item.height = t.outerHeight();
                }

                p = t.offset();
                item.left = p.left;
                item.top = p.top;
            }
        },

        refreshPositions: function( fast ) {

            // Determine whether items are being displayed horizontally
            this.floating = this.items.length ?
                this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
                false;

            // This has to be redone because due to the item being moved out/into the offsetParent,
            // the offsetParent's position will change
            if ( this.offsetParent && this.helper ) {
                this.offset.parent = this._getParentOffset();
            }

            this._refreshItemPositions( fast );

            var i, p;

            if ( this.options.custom && this.options.custom.refreshContainers ) {
                this.options.custom.refreshContainers.call( this );
            } else {
                for ( i = this.containers.length - 1; i >= 0; i-- ) {
                    p = this.containers[ i ].element.offset();
                    this.containers[ i ].containerCache.left = p.left;
                    this.containers[ i ].containerCache.top = p.top;
                    this.containers[ i ].containerCache.width =
                        this.containers[ i ].element.outerWidth();
                    this.containers[ i ].containerCache.height =
                        this.containers[ i ].element.outerHeight();
                }
            }

            return this;
        },

        _createPlaceholder: function( that ) {
            that = that || this;
            var className, nodeName,
                o = that.options;

            if ( !o.placeholder || o.placeholder.constructor === String ) {
                className = o.placeholder;
                nodeName = that.currentItem[ 0 ].nodeName.toLowerCase();
                o.placeholder = {
                    element: function() {

                        var element = $( "<" + nodeName + ">", that.document[ 0 ] );

                        that._addClass( element, "ui-sortable-placeholder",
                            className || that.currentItem[ 0 ].className )
                            ._removeClass( element, "ui-sortable-helper" );

                        if ( nodeName === "tbody" ) {
                            that._createTrPlaceholder(
                                that.currentItem.find( "tr" ).eq( 0 ),
                                $( "<tr>", that.document[ 0 ] ).appendTo( element )
                            );
                        } else if ( nodeName === "tr" ) {
                            that._createTrPlaceholder( that.currentItem, element );
                        } else if ( nodeName === "img" ) {
                            element.attr( "src", that.currentItem.attr( "src" ) );
                        }

                        if ( !className ) {
                            element.css( "visibility", "hidden" );
                        }

                        return element;
                    },
                    update: function( container, p ) {

                        // 1. If a className is set as 'placeholder option, we don't force sizes -
                        // the class is responsible for that
                        // 2. The option 'forcePlaceholderSize can be enabled to force it even if a
                        // class name is specified
                        if ( className && !o.forcePlaceholderSize ) {
                            return;
                        }

                        // If the element doesn't have a actual height or width by itself (without
                        // styles coming from a stylesheet), it receives the inline height and width
                        // from the dragged item. Or, if it's a tbody or tr, it's going to have a height
                        // anyway since we're populating them with <td>s above, but they're unlikely to
                        // be the correct height on their own if the row heights are dynamic, so we'll
                        // always assign the height of the dragged item given forcePlaceholderSize
                        // is true.
                        if ( !p.height() || ( o.forcePlaceholderSize &&
                            ( nodeName === "tbody" || nodeName === "tr" ) ) ) {
                            p.height(
                                that.currentItem.innerHeight() -
                                parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
                                parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
                        }
                        if ( !p.width() ) {
                            p.width(
                                that.currentItem.innerWidth() -
                                parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
                                parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
                        }
                    }
                };
            }

            //Create the placeholder
            that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );

            //Append it after the actual current item
            that.currentItem.after( that.placeholder );

            //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
            o.placeholder.update( that, that.placeholder );

        },

        _createTrPlaceholder: function( sourceTr, targetTr ) {
            var that = this;

            sourceTr.children().each( function() {
                $( "<td>&#160;</td>", that.document[ 0 ] )
                    .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
                    .appendTo( targetTr );
            } );
        },

        _contactContainers: function( event ) {
            var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
                floating, axis,
                innermostContainer = null,
                innermostIndex = null;

            // Get innermost container that intersects with item
            for ( i = this.containers.length - 1; i >= 0; i-- ) {

                // Never consider a container that's located within the item itself
                if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
                    continue;
                }

                if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {

                    // If we've already found a container and it's more "inner" than this, then continue
                    if ( innermostContainer &&
                        $.contains(
                            this.containers[ i ].element[ 0 ],
                            innermostContainer.element[ 0 ] ) ) {
                        continue;
                    }

                    innermostContainer = this.containers[ i ];
                    innermostIndex = i;

                } else {

                    // container doesn't intersect. trigger "out" event if necessary
                    if ( this.containers[ i ].containerCache.over ) {
                        this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
                        this.containers[ i ].containerCache.over = 0;
                    }
                }

            }

            // If no intersecting containers found, return
            if ( !innermostContainer ) {
                return;
            }

            // Move the item into the container if it's not there already
            if ( this.containers.length === 1 ) {
                if ( !this.containers[ innermostIndex ].containerCache.over ) {
                    this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
                    this.containers[ innermostIndex ].containerCache.over = 1;
                }
            } else {

                // When entering a new container, we will find the item with the least distance and
                // append our item near it
                dist = 10000;
                itemWithLeastDistance = null;
                floating = innermostContainer.floating || this._isFloating( this.currentItem );
                posProperty = floating ? "left" : "top";
                sizeProperty = floating ? "width" : "height";
                axis = floating ? "pageX" : "pageY";

                for ( j = this.items.length - 1; j >= 0; j-- ) {
                    if ( !$.contains(
                        this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
                    ) {
                        continue;
                    }
                    if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
                        continue;
                    }

                    cur = this.items[ j ].item.offset()[ posProperty ];
                    nearBottom = false;
                    if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
                        nearBottom = true;
                    }

                    if ( Math.abs( event[ axis ] - cur ) < dist ) {
                        dist = Math.abs( event[ axis ] - cur );
                        itemWithLeastDistance = this.items[ j ];
                        this.direction = nearBottom ? "up" : "down";
                    }
                }

                //Check if dropOnEmpty is enabled
                if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
                    return;
                }

                if ( this.currentContainer === this.containers[ innermostIndex ] ) {
                    if ( !this.currentContainer.containerCache.over ) {
                        this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
                        this.currentContainer.containerCache.over = 1;
                    }
                    return;
                }

                if ( itemWithLeastDistance ) {
                    this._rearrange( event, itemWithLeastDistance, null, true );
                } else {
                    this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
                }
                this._trigger( "change", event, this._uiHash() );
                this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
                this.currentContainer = this.containers[ innermostIndex ];

                //Update the placeholder
                this.options.placeholder.update( this.currentContainer, this.placeholder );

                //Update scrollParent
                this.scrollParent = this.placeholder.scrollParent();

                //Update overflowOffset
                if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                    this.scrollParent[ 0 ].tagName !== "HTML" ) {
                    this.overflowOffset = this.scrollParent.offset();
                }

                this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
                this.containers[ innermostIndex ].containerCache.over = 1;
            }

        },

        _createHelper: function( event ) {

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

            //Add the helper to the DOM if that didn't happen already
            if ( !helper.parents( "body" ).length ) {
                this.appendTo[ 0 ].appendChild( helper[ 0 ] );
            }

            if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
                this._storedCSS = {
                    width: this.currentItem[ 0 ].style.width,
                    height: this.currentItem[ 0 ].style.height,
                    position: this.currentItem.css( "position" ),
                    top: this.currentItem.css( "top" ),
                    left: this.currentItem.css( "left" )
                };
            }

            if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
                helper.width( this.currentItem.width() );
            }
            if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
                helper.height( this.currentItem.height() );
            }

            return helper;

        },

        _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;
            }
        },

        _getParentOffset: function() {

            //Get the offsetParent and cache its position
            this.offsetParent = this.helper.offsetParent();
            var po = this.offsetParent.offset();

            // 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 ] !== this.document[ 0 ] &&
                $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
                po.left += this.scrollParent.scrollLeft();
                po.top += this.scrollParent.scrollTop();
            }

            // This needs to be actually done for all browsers, since pageX/pageY includes this
            // information with an ugly IE fix
            if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
                ( this.offsetParent[ 0 ].tagName &&
                    this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
                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" ) {
                var p = this.currentItem.position();
                return {
                    top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
                        this.scrollParent.scrollTop(),
                    left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
                        this.scrollParent.scrollLeft()
                };
            } else {
                return { top: 0, left: 0 };
            }

        },

        _cacheMargins: function() {
            this.margins = {
                left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
                top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
            };
        },

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

        _setContainment: function() {

            var ce, co, over,
                o = this.options;
            if ( o.containment === "parent" ) {
                o.containment = this.helper[ 0 ].parentNode;
            }
            if ( o.containment === "document" || o.containment === "window" ) {
                this.containment = [
                    0 - this.offset.relative.left - this.offset.parent.left,
                    0 - this.offset.relative.top - this.offset.parent.top,
                    o.containment === "document" ?
                        this.document.width() :
                        this.window.width() - this.helperProportions.width - this.margins.left,
                    ( o.containment === "document" ?
                            ( this.document.height() || document.body.parentNode.scrollHeight ) :
                            this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
                    ) - this.helperProportions.height - this.margins.top
                ];
            }

            if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
                ce = $( o.containment )[ 0 ];
                co = $( o.containment ).offset();
                over = ( $( ce ).css( "overflow" ) !== "hidden" );

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

        },

        _convertPositionTo: function( d, pos ) {

            if ( !pos ) {
                pos = this.position;
            }
            var mod = d === "absolute" ? 1 : -1,
                scroll = this.cssPosition === "absolute" &&
                !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                    $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
                    this.offsetParent :
                    this.scrollParent,
                scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );

            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.scrollParent.scrollTop() :
                        ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * 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.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
                            scroll.scrollLeft() ) * mod )
                )
            };

        },

        _generatePosition: function( event ) {

            var top, left,
                o = this.options,
                pageX = event.pageX,
                pageY = event.pageY,
                scroll = this.cssPosition === "absolute" &&
                !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                    $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
                    this.offsetParent :
                    this.scrollParent,
                scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );

            // This is another very weird special case that only happens for relative elements:
            // 1. If the css position is relative
            // 2. and the scroll parent is the document or similar to the offset parent
            // we have to refresh the relative offset during the scroll so there are no jumps
            if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
                this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
                this.offset.relative = this._getRelativeOffset();
            }

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

            if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options

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

                if ( o.grid ) {
                    top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
                        o.grid[ 1 ] ) * o.grid[ 1 ];
                    pageY = this.containment ?
                        ( ( top - this.offset.click.top >= this.containment[ 1 ] &&
                            top - this.offset.click.top <= this.containment[ 3 ] ) ?
                            top :
                            ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
                                top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
                        top;

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

            }

            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.scrollParent.scrollTop() :
                        ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
                ),
                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.scrollParent.scrollLeft() :
                        scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
                )
            };

        },

        _rearrange: function( event, i, a, hardRefresh ) {

            if ( a ) {
                a[ 0 ].appendChild( this.placeholder[ 0 ] );
            } else {
                i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
                    ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
            }

            //Various things done here to improve the performance:
            // 1. we create a setTimeout, that calls refreshPositions
            // 2. on the instance, we have a counter variable, that get's higher after every append
            // 3. on the local scope, we copy the counter variable, and check in the timeout,
            // if it's still the same
            // 4. this lets only the last addition to the timeout stack through
            this.counter = this.counter ? ++this.counter : 1;
            var counter = this.counter;

            this._delay( function() {
                if ( counter === this.counter ) {

                    //Precompute after each DOM insertion, NOT on mousemove
                    this.refreshPositions( !hardRefresh );
                }
            } );

        },

        _clear: function( event, noPropagation ) {

            this.reverting = false;

            // We delay all events that have to be triggered to after the point where the placeholder
            // has been removed and everything else normalized again
            var i,
                delayedTriggers = [];

            // We first have to update the dom position of the actual currentItem
            // Note: don't do it if the current item is already removed (by a user), or it gets
            // reappended (see #4088)
            if ( !this._noFinalSort && this.currentItem.parent().length ) {
                this.placeholder.before( this.currentItem );
            }
            this._noFinalSort = null;

            if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
                for ( i in this._storedCSS ) {
                    if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
                        this._storedCSS[ i ] = "";
                    }
                }
                this.currentItem.css( this._storedCSS );
                this._removeClass( this.currentItem, "ui-sortable-helper" );
            } else {
                this.currentItem.show();
            }

            if ( this.fromOutside && !noPropagation ) {
                delayedTriggers.push( function( event ) {
                    this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
                } );
            }
            if ( ( this.fromOutside ||
                this.domPosition.prev !==
                this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
                this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {

                // Trigger update callback if the DOM position has changed
                delayedTriggers.push( function( event ) {
                    this._trigger( "update", event, this._uiHash() );
                } );
            }

            // Check if the items Container has Changed and trigger appropriate
            // events.
            if ( this !== this.currentContainer ) {
                if ( !noPropagation ) {
                    delayedTriggers.push( function( event ) {
                        this._trigger( "remove", event, this._uiHash() );
                    } );
                    delayedTriggers.push( ( function( c ) {
                        return function( event ) {
                            c._trigger( "receive", event, this._uiHash( this ) );
                        };
                    } ).call( this, this.currentContainer ) );
                    delayedTriggers.push( ( function( c ) {
                        return function( event ) {
                            c._trigger( "update", event, this._uiHash( this ) );
                        };
                    } ).call( this, this.currentContainer ) );
                }
            }

            //Post events to containers
            function delayEvent( type, instance, container ) {
                return function( event ) {
                    container._trigger( type, event, instance._uiHash( instance ) );
                };
            }
            for ( i = this.containers.length - 1; i >= 0; i-- ) {
                if ( !noPropagation ) {
                    delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
                }
                if ( this.containers[ i ].containerCache.over ) {
                    delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
                    this.containers[ i ].containerCache.over = 0;
                }
            }

            //Do what was originally in plugins
            if ( this.storedCursor ) {
                this.document.find( "body" ).css( "cursor", this.storedCursor );
                this.storedStylesheet.remove();
            }
            if ( this._storedOpacity ) {
                this.helper.css( "opacity", this._storedOpacity );
            }
            if ( this._storedZIndex ) {
                this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
            }

            this.dragging = false;

            if ( !noPropagation ) {
                this._trigger( "beforeStop", event, this._uiHash() );
            }

            //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
            // it unbinds ALL events from the original node!
            this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );

            if ( !this.cancelHelperRemoval ) {
                if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
                    this.helper.remove();
                }
                this.helper = null;
            }

            if ( !noPropagation ) {
                for ( i = 0; i < delayedTriggers.length; i++ ) {

                    // Trigger all delayed events
                    delayedTriggers[ i ].call( this, event );
                }
                this._trigger( "stop", event, this._uiHash() );
            }

            this.fromOutside = false;
            return !this.cancelHelperRemoval;

        },

        _trigger: function() {
            if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
                this.cancel();
            }
        },

        _uiHash: function( _inst ) {
            var inst = _inst || this;
            return {
                helper: inst.helper,
                placeholder: inst.placeholder || $( [] ),
                position: inst.position,
                originalPosition: inst.originalPosition,
                offset: inst.positionAbs,
                item: inst.currentItem,
                sender: _inst ? _inst.element : null
            };
        }

    } );

} );

/*!
 * 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);
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Banner/js/model/banner',[
    'jquery',
    'underscore',
    'ko',
    'jquery/jquery-storageapi'
], function ($, _, ko) {
    'use strict';

    var options = {
            cacheTtl: 0,
            sectionLoadUrl: ''
        },
        selectors = {
            productIdSelector: '#product_addtocart_form [name="product"]'
        },
        storage = $.initNamespaceStorage('mage-banners-cache-storage').localStorage,
        productId = $(selectors.productIdSelector).val(),

        /**
         * Cache invalidation. Banner cache ttl is 30 sec by default.
         */
        invalidateCacheBySessionTimeOut = function () {
            var cacheEol = new Date($.localStorage.get('mage-banners-cache-timeout')),
                dateTo = new Date(Date.now() + options.cacheTtl),
                cartDataId = null,
                globalStoreId = $.cookieStorage.get('store') || 'default';

            if ($.localStorage.get('mage-banners-storeId') === null) {
                $.localStorage.set('mage-banners-storeId', globalStoreId);
            }

            if ($.localStorage.get('mage-cache-storage') !== null &&
                $.localStorage.get('mage-cache-storage').hasOwnProperty('cart')) {
                cartDataId = $.localStorage.get('mage-cache-storage').cart['data_id'];
            }

            if (cacheEol < new Date() ||
                $.localStorage.get('mage-banners-storeId') !== globalStoreId ||
                $.localStorage.get('mage-banners-cartDataId') !== cartDataId) {
                storage.removeAll();
                $.localStorage.set('mage-banners-cache-timeout', dateTo);
                $.localStorage.set('mage-banners-storeId', globalStoreId);

                if (cartDataId) {
                    $.localStorage.set('mage-banners-cartDataId', cartDataId);
                }
            }
        },
        dataProvider = {

            /**
             * Request data from storage
             *
             * @param {Array} sectionNames
             * @returns {Object}
             */
            getFromStorage: function (sectionNames) {
                var result = {};

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

                return result;
            },

            /**
             * Request data from server
             *
             * @param {Array} sectionNames
             * @returns {Object}
             */
            getFromServer: function (sectionNames) {
                var parameters = {
                    'requesting_page_url': window.location.href
                };

                if (productId) {
                    parameters.data = {
                        'product_id': productId
                    };
                }

                if (_.isArray(sectionNames)) {
                    parameters.sections = sectionNames.join(',');
                }

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

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

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

                return this.data[sectionName];
            },

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

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

            /**
             * Update sections
             *
             * @param {Array} sections
             */
            update: function (sections) {
                _.each(sections, function (sectionData, sectionName) {
                    storage.set(sectionName, sectionData);
                    buffer.notify(sectionName, sectionData);
                });
            }
        },
        banner = {

            /**
             * Initialization
             */
            init: function () {
                var dataSectionName;

                dataSectionName = this.getSectionName('data');

                if (_.isEmpty(storage.keys()) || !storage.get(dataSectionName)) {
                    this.reload([]);
                } else {
                    _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
                        buffer.notify(sectionName, sectionData);
                    });
                }
            },

            /**
             * Get data
             *
             * @param {String} sectionName
             * @returns {*|Object}
             */
            get: function (sectionName) {
                sectionName = this.getSectionName(sectionName);

                return buffer.get(sectionName);
            },

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

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

            /**
             * Reloading from storage or server
             *
             * @param {Array} sectionNames
             * @returns {Object}
             */
            reload: function (sectionNames) {
                return dataProvider.getFromServer(sectionNames).done(function (sections) {
                    _.each(sections, function (sectionData, sectionName) {
                        this.set(sectionName, sectionData);
                    }.bind(this));
                }.bind(this));
            },

            /**
             * Get section name
             *
             * @returns {String}
             */
            getSectionName: function (sectionName) {
                var result;

                result = productId && sectionName === 'data' ? 'data_product_' + productId : sectionName;

                return result;
            },

            /**
             * Init helper
             *
             * @param {Array} settings
             */
            'Magento_Banner/js/model/banner': function (settings) {
                options = _.extend(options, settings);
                invalidateCacheBySessionTimeOut(settings);
                banner.init();
            }
        };

    //TODO: remove global change, in this case made for initNamespaceStorage
    $.cookieStorage.setConf({
        path: '/'
    });

    return banner;
});

/**
 * 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';});

/*!
 * 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;
});

define('Magento_Theme/js/cookie-status',[
    'jquery',
    'Magento_Ui/js/modal/modal',
    'mage/translate'
], function ($, modal) {
    'use strict';

    $.widget('mage.cookieStatus', {
        options: {
            type: 'popup',
            responsive: true,
            innerScroll: true,
            autoOpen: true,
            buttons: [{
                text: $.mage.__('Close'),
                class: 'cookie-status',

                /**
                 * Callback for click event
                 */
                click: function () {
                    this.closeModal();
                }
            }]
        },

        /**
         * Init object
         * @private
         */
        _init: function () {

            if (!navigator.cookieEnabled) {
                modal(this.options, $('#cookie-status'));
            }
        }
    });

    return $.mage.cookieStatus;
});

// Spectrum Colorpicker v1.8.1
// https://github.com/bgrins/spectrum
// Author: Brian Grinstead
// License: MIT

(function (factory) {
    "use strict";

    if (typeof define === 'function' && define.amd) { // AMD
        define('spectrum',['jquery'], factory);
    }
    else if (typeof exports == "object" && typeof module == "object") { // CommonJS
        module.exports = factory(require('jquery'));
    }
    else { // Browser
        factory(jQuery);
    }
})(function($, undefined) {
    "use strict";

    var defaultOpts = {

            // Callbacks
            beforeShow: noop,
            move: noop,
            change: noop,
            show: noop,
            hide: noop,

            // Options
            color: false,
            flat: false,
            showInput: false,
            allowEmpty: false,
            showButtons: true,
            clickoutFiresChange: true,
            showInitial: false,
            showPalette: false,
            showPaletteOnly: false,
            hideAfterPaletteSelect: false,
            togglePaletteOnly: false,
            showSelectionPalette: true,
            localStorageKey: false,
            appendTo: "body",
            maxSelectionSize: 7,
            cancelText: "cancel",
            chooseText: "choose",
            togglePaletteMoreText: "more",
            togglePaletteLessText: "less",
            clearText: "Clear Color Selection",
            noColorSelectedText: "No Color Selected",
            preferredFormat: false,
            className: "", // Deprecated - use containerClassName and replacerClassName instead.
            containerClassName: "",
            replacerClassName: "",
            showAlpha: false,
            theme: "sp-light",
            palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]],
            selectionPalette: [],
            disabled: false,
            offset: null
        },
        spectrums = [],
        IE = !!/msie/i.exec( window.navigator.userAgent ),
        rgbaSupport = (function() {
            function contains( str, substr ) {
                return !!~('' + str).indexOf(substr);
            }

            var elem = document.createElement('div');
            var style = elem.style;
            style.cssText = 'background-color:rgba(0,0,0,.5)';
            return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
        })(),
        replaceInput = [
            "<div class='sp-replacer'>",
            "<div class='sp-preview'><div class='sp-preview-inner'></div></div>",
            "<div class='sp-dd'>&#9660;</div>",
            "</div>"
        ].join(''),
        markup = (function () {

            // IE does not support gradients with multiple stops, so we need to simulate
            //  that for the rainbow slider with 8 divs that each have a single gradient
            var gradientFix = "";
            if (IE) {
                for (var i = 1; i <= 6; i++) {
                    gradientFix += "<div class='sp-" + i + "'></div>";
                }
            }

            return [
                "<div class='sp-container sp-hidden'>",
                "<div class='sp-palette-container'>",
                "<div class='sp-palette sp-thumb sp-cf'></div>",
                "<div class='sp-palette-button-container sp-cf'>",
                "<button type='button' class='sp-palette-toggle'></button>",
                "</div>",
                "</div>",
                "<div class='sp-picker-container'>",
                "<div class='sp-top sp-cf'>",
                "<div class='sp-fill'></div>",
                "<div class='sp-top-inner'>",
                "<div class='sp-color'>",
                "<div class='sp-sat'>",
                "<div class='sp-val'>",
                "<div class='sp-dragger'></div>",
                "</div>",
                "</div>",
                "</div>",
                "<div class='sp-clear sp-clear-display'>",
                "</div>",
                "<div class='sp-hue'>",
                "<div class='sp-slider'></div>",
                gradientFix,
                "</div>",
                "</div>",
                "<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>",
                "</div>",
                "<div class='sp-input-container sp-cf'>",
                "<input class='sp-input' type='text' spellcheck='false'  />",
                "</div>",
                "<div class='sp-initial sp-thumb sp-cf'></div>",
                "<div class='sp-button-container sp-cf'>",
                "<a class='sp-cancel' href='#'></a>",
                "<button type='button' class='sp-choose'></button>",
                "</div>",
                "</div>",
                "</div>"
            ].join("");
        })();

    function paletteTemplate (p, color, className, opts) {
        var html = [];
        for (var i = 0; i < p.length; i++) {
            var current = p[i];
            if(current) {
                var tiny = tinycolor(current);
                var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
                c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : "";
                var formattedString = tiny.toString(opts.preferredFormat || "rgb");
                var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
                html.push('<span title="' + formattedString + '" data-color="' + tiny.toRgbString() + '" class="' + c + '"><span class="sp-thumb-inner" style="' + swatchStyle + ';"></span></span>');
            } else {
                var cls = 'sp-clear-display';
                html.push($('<div />')
                    .append($('<span data-color="" style="background-color:transparent;" class="' + cls + '"></span>')
                        .attr('title', opts.noColorSelectedText)
                    )
                    .html()
                );
            }
        }
        return "<div class='sp-cf " + className + "'>" + html.join('') + "</div>";
    }

    function hideAll() {
        for (var i = 0; i < spectrums.length; i++) {
            if (spectrums[i]) {
                spectrums[i].hide();
            }
        }
    }

    function instanceOptions(o, callbackContext) {
        var opts = $.extend({}, defaultOpts, o);
        opts.callbacks = {
            'move': bind(opts.move, callbackContext),
            'change': bind(opts.change, callbackContext),
            'show': bind(opts.show, callbackContext),
            'hide': bind(opts.hide, callbackContext),
            'beforeShow': bind(opts.beforeShow, callbackContext)
        };

        return opts;
    }

    function spectrum(element, o) {

        var opts = instanceOptions(o, element),
            flat = opts.flat,
            showSelectionPalette = opts.showSelectionPalette,
            localStorageKey = opts.localStorageKey,
            theme = opts.theme,
            callbacks = opts.callbacks,
            resize = throttle(reflow, 10),
            visible = false,
            isDragging = false,
            dragWidth = 0,
            dragHeight = 0,
            dragHelperHeight = 0,
            slideHeight = 0,
            slideWidth = 0,
            alphaWidth = 0,
            alphaSlideHelperWidth = 0,
            slideHelperHeight = 0,
            currentHue = 0,
            currentSaturation = 0,
            currentValue = 0,
            currentAlpha = 1,
            palette = [],
            paletteArray = [],
            paletteLookup = {},
            selectionPalette = opts.selectionPalette.slice(0),
            maxSelectionSize = opts.maxSelectionSize,
            draggingClass = "sp-dragging",
            shiftMovementDirection = null;

        var doc = element.ownerDocument,
            body = doc.body,
            boundElement = $(element),
            disabled = false,
            container = $(markup, doc).addClass(theme),
            pickerContainer = container.find(".sp-picker-container"),
            dragger = container.find(".sp-color"),
            dragHelper = container.find(".sp-dragger"),
            slider = container.find(".sp-hue"),
            slideHelper = container.find(".sp-slider"),
            alphaSliderInner = container.find(".sp-alpha-inner"),
            alphaSlider = container.find(".sp-alpha"),
            alphaSlideHelper = container.find(".sp-alpha-handle"),
            textInput = container.find(".sp-input"),
            paletteContainer = container.find(".sp-palette"),
            initialColorContainer = container.find(".sp-initial"),
            cancelButton = container.find(".sp-cancel"),
            clearButton = container.find(".sp-clear"),
            chooseButton = container.find(".sp-choose"),
            toggleButton = container.find(".sp-palette-toggle"),
            isInput = boundElement.is("input"),
            isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(),
            shouldReplace = isInput && !flat,
            replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),
            offsetElement = (shouldReplace) ? replacer : boundElement,
            previewElement = replacer.find(".sp-preview-inner"),
            initialColor = opts.color || (isInput && boundElement.val()),
            colorOnShow = false,
            currentPreferredFormat = opts.preferredFormat,
            clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,
            isEmpty = !initialColor,
            allowEmpty = opts.allowEmpty && !isInputTypeColor;

        function applyOptions() {

            if (opts.showPaletteOnly) {
                opts.showPalette = true;
            }

            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);

            if (opts.palette) {
                palette = opts.palette.slice(0);
                paletteArray = $.isArray(palette[0]) ? palette : [palette];
                paletteLookup = {};
                for (var i = 0; i < paletteArray.length; i++) {
                    for (var j = 0; j < paletteArray[i].length; j++) {
                        var rgb = tinycolor(paletteArray[i][j]).toRgbString();
                        paletteLookup[rgb] = true;
                    }
                }
            }

            container.toggleClass("sp-flat", flat);
            container.toggleClass("sp-input-disabled", !opts.showInput);
            container.toggleClass("sp-alpha-enabled", opts.showAlpha);
            container.toggleClass("sp-clear-enabled", allowEmpty);
            container.toggleClass("sp-buttons-disabled", !opts.showButtons);
            container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly);
            container.toggleClass("sp-palette-disabled", !opts.showPalette);
            container.toggleClass("sp-palette-only", opts.showPaletteOnly);
            container.toggleClass("sp-initial-disabled", !opts.showInitial);
            container.addClass(opts.className).addClass(opts.containerClassName);

            reflow();
        }

        function initialize() {

            if (IE) {
                container.find("*:not(input)").attr("unselectable", "on");
            }

            applyOptions();

            if (shouldReplace) {
                boundElement.after(replacer).hide();
            }

            if (!allowEmpty) {
                clearButton.hide();
            }

            if (flat) {
                boundElement.after(container).hide();
            }
            else {

                var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo);
                if (appendTo.length !== 1) {
                    appendTo = $("body");
                }

                appendTo.append(container);
            }

            updateSelectionPaletteFromStorage();

            offsetElement.on("click.spectrum touchstart.spectrum", function (e) {
                if (!disabled) {
                    toggle();
                }

                e.stopPropagation();

                if (!$(e.target).is("input")) {
                    e.preventDefault();
                }
            });

            if(boundElement.is(":disabled") || (opts.disabled === true)) {
                disable();
            }

            // Prevent clicks from bubbling up to document.  This would cause it to be hidden.
            container.click(stopPropagation);

            // Handle user typed input
            textInput.change(setFromTextInput);
            textInput.on("paste", function () {
                setTimeout(setFromTextInput, 1);
            });
            textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });

            cancelButton.text(opts.cancelText);
            cancelButton.on("click.spectrum", function (e) {
                e.stopPropagation();
                e.preventDefault();
                revert();
                hide();
            });

            clearButton.attr("title", opts.clearText);
            clearButton.on("click.spectrum", function (e) {
                e.stopPropagation();
                e.preventDefault();
                isEmpty = true;
                move();

                if(flat) {
                    //for the flat style, this is a change event
                    updateOriginalInput(true);
                }
            });

            chooseButton.text(opts.chooseText);
            chooseButton.on("click.spectrum", function (e) {
                e.stopPropagation();
                e.preventDefault();

                if (IE && textInput.is(":focus")) {
                    textInput.trigger('change');
                }

                if (isValid()) {
                    updateOriginalInput(true);
                    hide();
                }
            });

            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
            toggleButton.on("click.spectrum", function (e) {
                e.stopPropagation();
                e.preventDefault();

                opts.showPaletteOnly = !opts.showPaletteOnly;

                // To make sure the Picker area is drawn on the right, next to the
                // Palette area (and not below the palette), first move the Palette
                // to the left to make space for the picker, plus 5px extra.
                // The 'applyOptions' function puts the whole container back into place
                // and takes care of the button-text and the sp-palette-only CSS class.
                if (!opts.showPaletteOnly && !flat) {
                    container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));
                }
                applyOptions();
            });

            draggable(alphaSlider, function (dragX, dragY, e) {
                currentAlpha = (dragX / alphaWidth);
                isEmpty = false;
                if (e.shiftKey) {
                    currentAlpha = Math.round(currentAlpha * 10) / 10;
                }

                move();
            }, dragStart, dragStop);

            draggable(slider, function (dragX, dragY) {
                currentHue = parseFloat(dragY / slideHeight);
                isEmpty = false;
                if (!opts.showAlpha) {
                    currentAlpha = 1;
                }
                move();
            }, dragStart, dragStop);

            draggable(dragger, function (dragX, dragY, e) {

                // shift+drag should snap the movement to either the x or y axis.
                if (!e.shiftKey) {
                    shiftMovementDirection = null;
                }
                else if (!shiftMovementDirection) {
                    var oldDragX = currentSaturation * dragWidth;
                    var oldDragY = dragHeight - (currentValue * dragHeight);
                    var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);

                    shiftMovementDirection = furtherFromX ? "x" : "y";
                }

                var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x";
                var setValue = !shiftMovementDirection || shiftMovementDirection === "y";

                if (setSaturation) {
                    currentSaturation = parseFloat(dragX / dragWidth);
                }
                if (setValue) {
                    currentValue = parseFloat((dragHeight - dragY) / dragHeight);
                }

                isEmpty = false;
                if (!opts.showAlpha) {
                    currentAlpha = 1;
                }

                move();

            }, dragStart, dragStop);

            if (!!initialColor) {
                set(initialColor);

                // In case color was black - update the preview UI and set the format
                // since the set function will not run (default color is black).
                updateUI();
                currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;

                addColorToSelectionPalette(initialColor);
            }
            else {
                updateUI();
            }

            if (flat) {
                show();
            }

            function paletteElementClick(e) {
                if (e.data && e.data.ignore) {
                    set($(e.target).closest(".sp-thumb-el").data("color"));
                    move();
                }
                else {
                    set($(e.target).closest(".sp-thumb-el").data("color"));
                    move();

                    updateOriginalInput(true);
                    if (opts.hideAfterPaletteSelect) {
                        hide();
                    }
                }

                return false;
            }

            var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
            paletteContainer.on(paletteEvent, ".sp-thumb-el", paletteElementClick);
            initialColorContainer.on(paletteEvent, ".sp-thumb-el:nth-child(1)", { ignore: true }, paletteElementClick);
        }

        function updateSelectionPaletteFromStorage() {

            if (localStorageKey && window.localStorage) {

                // Migrate old palettes over to new format.  May want to remove this eventually.
                try {
                    var oldPalette = window.localStorage[localStorageKey].split(",#");
                    if (oldPalette.length > 1) {
                        delete window.localStorage[localStorageKey];
                        $.each(oldPalette, function(i, c) {
                            addColorToSelectionPalette(c);
                        });
                    }
                }
                catch(e) { }

                try {
                    selectionPalette = window.localStorage[localStorageKey].split(";");
                }
                catch (e) { }
            }
        }

        function addColorToSelectionPalette(color) {
            if (showSelectionPalette) {
                var rgb = tinycolor(color).toRgbString();
                if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
                    selectionPalette.push(rgb);
                    while(selectionPalette.length > maxSelectionSize) {
                        selectionPalette.shift();
                    }
                }

                if (localStorageKey && window.localStorage) {
                    try {
                        window.localStorage[localStorageKey] = selectionPalette.join(";");
                    }
                    catch(e) { }
                }
            }
        }

        function getUniqueSelectionPalette() {
            var unique = [];
            if (opts.showPalette) {
                for (var i = 0; i < selectionPalette.length; i++) {
                    var rgb = tinycolor(selectionPalette[i]).toRgbString();

                    if (!paletteLookup[rgb]) {
                        unique.push(selectionPalette[i]);
                    }
                }
            }

            return unique.reverse().slice(0, opts.maxSelectionSize);
        }

        function drawPalette() {

            var currentColor = get();

            var html = $.map(paletteArray, function (palette, i) {
                return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts);
            });

            updateSelectionPaletteFromStorage();

            if (selectionPalette) {
                html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts));
            }

            paletteContainer.html(html.join(""));
        }

        function drawInitial() {
            if (opts.showInitial) {
                var initial = colorOnShow;
                var current = get();
                initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts));
            }
        }

        function dragStart() {
            if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
                reflow();
            }
            isDragging = true;
            container.addClass(draggingClass);
            shiftMovementDirection = null;
            boundElement.trigger('dragstart.spectrum', [ get() ]);
        }

        function dragStop() {
            isDragging = false;
            container.removeClass(draggingClass);
            boundElement.trigger('dragstop.spectrum', [ get() ]);
        }

        function setFromTextInput() {

            var value = textInput.val();

            if ((value === null || value === "") && allowEmpty) {
                set(null);
                move();
                updateOriginalInput();
            }
            else {
                var tiny = tinycolor(value);
                if (tiny.isValid()) {
                    set(tiny);
                    move();
                    updateOriginalInput(true);
                }
                else {
                    textInput.addClass("sp-validation-error");
                }
            }
        }

        function toggle() {
            if (visible) {
                hide();
            }
            else {
                show();
            }
        }

        function show() {
            var event = $.Event('beforeShow.spectrum');

            if (visible) {
                reflow();
                return;
            }

            boundElement.trigger(event, [ get() ]);

            if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {
                return;
            }

            hideAll();
            visible = true;

            $(doc).on("keydown.spectrum", onkeydown);
            $(doc).on("click.spectrum", clickout);
            $(window).on("resize.spectrum", resize);
            replacer.addClass("sp-active");
            container.removeClass("sp-hidden");

            reflow();
            updateUI();

            colorOnShow = get();

            drawInitial();
            callbacks.show(colorOnShow);
            boundElement.trigger('show.spectrum', [ colorOnShow ]);
        }

        function onkeydown(e) {
            // Close on ESC
            if (e.keyCode === 27) {
                hide();
            }
        }

        function clickout(e) {
            // Return on right click.
            if (e.button == 2) { return; }

            // If a drag event was happening during the mouseup, don't hide
            // on click.
            if (isDragging) { return; }

            if (clickoutFiresChange) {
                updateOriginalInput(true);
            }
            else {
                revert();
            }
            hide();
        }

        function hide() {
            // Return if hiding is unnecessary
            if (!visible || flat) { return; }
            visible = false;

            $(doc).off("keydown.spectrum", onkeydown);
            $(doc).off("click.spectrum", clickout);
            $(window).off("resize.spectrum", resize);

            replacer.removeClass("sp-active");
            container.addClass("sp-hidden");

            callbacks.hide(get());
            boundElement.trigger('hide.spectrum', [ get() ]);
        }

        function revert() {
            set(colorOnShow, true);
            updateOriginalInput(true);
        }

        function set(color, ignoreFormatChange) {
            if (tinycolor.equals(color, get())) {
                // Update UI just in case a validation error needs
                // to be cleared.
                updateUI();
                return;
            }

            var newColor, newHsv;
            if (!color && allowEmpty) {
                isEmpty = true;
            } else {
                isEmpty = false;
                newColor = tinycolor(color);
                newHsv = newColor.toHsv();

                currentHue = (newHsv.h % 360) / 360;
                currentSaturation = newHsv.s;
                currentValue = newHsv.v;
                currentAlpha = newHsv.a;
            }
            updateUI();

            if (newColor && newColor.isValid() && !ignoreFormatChange) {
                currentPreferredFormat = opts.preferredFormat || newColor.getFormat();
            }
        }

        function get(opts) {
            opts = opts || { };

            if (allowEmpty && isEmpty) {
                return null;
            }

            return tinycolor.fromRatio({
                h: currentHue,
                s: currentSaturation,
                v: currentValue,
                a: Math.round(currentAlpha * 1000) / 1000
            }, { format: opts.format || currentPreferredFormat });
        }

        function isValid() {
            return !textInput.hasClass("sp-validation-error");
        }

        function move() {
            updateUI();

            callbacks.move(get());
            boundElement.trigger('move.spectrum', [ get() ]);
        }

        function updateUI() {

            textInput.removeClass("sp-validation-error");

            updateHelperLocations();

            // Update dragger background color (gradients take care of saturation and value).
            var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });
            dragger.css("background-color", flatColor.toHexString());

            // Get a format that alpha will be included in (hex and names ignore alpha)
            var format = currentPreferredFormat;
            if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) {
                if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") {
                    format = "rgb";
                }
            }

            var realColor = get({ format: format }),
                displayColor = '';

            //reset background info for preview element
            previewElement.removeClass("sp-clear-display");
            previewElement.css('background-color', 'transparent');

            if (!realColor && allowEmpty) {
                // Update the replaced elements background with icon indicating no color selection
                previewElement.addClass("sp-clear-display");
            }
            else {
                var realHex = realColor.toHexString(),
                    realRgb = realColor.toRgbString();

                // Update the replaced elements background color (with actual selected color)
                if (rgbaSupport || realColor.alpha === 1) {
                    previewElement.css("background-color", realRgb);
                }
                else {
                    previewElement.css("background-color", "transparent");
                    previewElement.css("filter", realColor.toFilter());
                }

                if (opts.showAlpha) {
                    var rgb = realColor.toRgb();
                    rgb.a = 0;
                    var realAlpha = tinycolor(rgb).toRgbString();
                    var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";

                    if (IE) {
                        alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
                    }
                    else {
                        alphaSliderInner.css("background", "-webkit-" + gradient);
                        alphaSliderInner.css("background", "-moz-" + gradient);
                        alphaSliderInner.css("background", "-ms-" + gradient);
                        // Use current syntax gradient on unprefixed property.
                        alphaSliderInner.css("background",
                            "linear-gradient(to right, " + realAlpha + ", " + realHex + ")");
                    }
                }

                displayColor = realColor.toString(format);
            }

            // Update the text entry input as it changes happen
            if (opts.showInput) {
                textInput.val(displayColor);
            }

            if (opts.showPalette) {
                drawPalette();
            }

            drawInitial();
        }

        function updateHelperLocations() {
            var s = currentSaturation;
            var v = currentValue;

            if(allowEmpty && isEmpty) {
                //if selected color is empty, hide the helpers
                alphaSlideHelper.hide();
                slideHelper.hide();
                dragHelper.hide();
            }
            else {
                //make sure helpers are visible
                alphaSlideHelper.show();
                slideHelper.show();
                dragHelper.show();

                // Where to show the little circle in that displays your current selected color
                var dragX = s * dragWidth;
                var dragY = dragHeight - (v * dragHeight);
                dragX = Math.max(
                    -dragHelperHeight,
                    Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
                );
                dragY = Math.max(
                    -dragHelperHeight,
                    Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
                );
                dragHelper.css({
                    "top": dragY + "px",
                    "left": dragX + "px"
                });

                var alphaX = currentAlpha * alphaWidth;
                alphaSlideHelper.css({
                    "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px"
                });

                // Where to show the bar that displays your current selected hue
                var slideY = (currentHue) * slideHeight;
                slideHelper.css({
                    "top": (slideY - slideHelperHeight) + "px"
                });
            }
        }

        function updateOriginalInput(fireCallback) {
            var color = get(),
                displayColor = '',
                hasChanged = !tinycolor.equals(color, colorOnShow);

            if (color) {
                displayColor = color.toString(currentPreferredFormat);
                // Update the selection palette with the current color
                addColorToSelectionPalette(color);
            }

            if (isInput) {
                boundElement.val(displayColor);
            }

            if (fireCallback && hasChanged) {
                callbacks.change(color);
                boundElement.trigger('change', [ color ]);
            }
        }

        function reflow() {
            if (!visible) {
                return; // Calculations would be useless and wouldn't be reliable anyways
            }
            dragWidth = dragger.width();
            dragHeight = dragger.height();
            dragHelperHeight = dragHelper.height();
            slideWidth = slider.width();
            slideHeight = slider.height();
            slideHelperHeight = slideHelper.height();
            alphaWidth = alphaSlider.width();
            alphaSlideHelperWidth = alphaSlideHelper.width();

            if (!flat) {
                container.css("position", "absolute");
                if (opts.offset) {
                    container.offset(opts.offset);
                } else {
                    container.offset(getOffset(container, offsetElement));
                }
            }

            updateHelperLocations();

            if (opts.showPalette) {
                drawPalette();
            }

            boundElement.trigger('reflow.spectrum');
        }

        function destroy() {
            boundElement.show();
            offsetElement.off("click.spectrum touchstart.spectrum");
            container.remove();
            replacer.remove();
            spectrums[spect.id] = null;
        }

        function option(optionName, optionValue) {
            if (optionName === undefined) {
                return $.extend({}, opts);
            }
            if (optionValue === undefined) {
                return opts[optionName];
            }

            opts[optionName] = optionValue;

            if (optionName === "preferredFormat") {
                currentPreferredFormat = opts.preferredFormat;
            }
            applyOptions();
        }

        function enable() {
            disabled = false;
            boundElement.attr("disabled", false);
            offsetElement.removeClass("sp-disabled");
        }

        function disable() {
            hide();
            disabled = true;
            boundElement.attr("disabled", true);
            offsetElement.addClass("sp-disabled");
        }

        function setOffset(coord) {
            opts.offset = coord;
            reflow();
        }

        initialize();

        var spect = {
            show: show,
            hide: hide,
            toggle: toggle,
            reflow: reflow,
            option: option,
            enable: enable,
            disable: disable,
            offset: setOffset,
            set: function (c) {
                set(c);
                updateOriginalInput();
            },
            get: get,
            destroy: destroy,
            container: container
        };

        spect.id = spectrums.push(spect) - 1;

        return spect;
    }

    /**
     * checkOffset - get the offset below/above and left/right element depending on screen position
     * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
     */
    function getOffset(picker, input) {
        var extraY = 0;
        var dpWidth = picker.outerWidth();
        var dpHeight = picker.outerHeight();
        var inputHeight = input.outerHeight();
        var doc = picker[0].ownerDocument;
        var docElem = doc.documentElement;
        var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
        var viewHeight = docElem.clientHeight + $(doc).scrollTop();
        var offset = input.offset();
        var offsetLeft = offset.left;
        var offsetTop = offset.top;

        offsetTop += inputHeight;

        offsetLeft -=
            Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ?
                Math.abs(offsetLeft + dpWidth - viewWidth) : 0);

        offsetTop -=
            Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ?
                Math.abs(dpHeight + inputHeight - extraY) : extraY));

        return {
            top: offsetTop,
            bottom: offset.bottom,
            left: offsetLeft,
            right: offset.right,
            width: offset.width,
            height: offset.height
        };
    }

    /**
     * noop - do nothing
     */
    function noop() {

    }

    /**
     * stopPropagation - makes the code only doing this a little easier to read in line
     */
    function stopPropagation(e) {
        e.stopPropagation();
    }

    /**
     * Create a function bound to a given object
     * Thanks to underscore.js
     */
    function bind(func, obj) {
        var slice = Array.prototype.slice;
        var args = slice.call(arguments, 2);
        return function () {
            return func.apply(obj, args.concat(slice.call(arguments)));
        };
    }

    /**
     * Lightweight drag helper.  Handles containment within the element, so that
     * when dragging, the x is within [0,element.width] and y is within [0,element.height]
     */
    function draggable(element, onmove, onstart, onstop) {
        onmove = onmove || function () { };
        onstart = onstart || function () { };
        onstop = onstop || function () { };
        var doc = document;
        var dragging = false;
        var offset = {};
        var maxHeight = 0;
        var maxWidth = 0;
        var hasTouch = ('ontouchstart' in window);

        var duringDragEvents = {};
        duringDragEvents["selectstart"] = prevent;
        duringDragEvents["dragstart"] = prevent;
        duringDragEvents["touchmove mousemove"] = move;
        duringDragEvents["touchend mouseup"] = stop;

        function prevent(e) {
            if (e.stopPropagation) {
                e.stopPropagation();
            }
            if (e.preventDefault) {
                e.preventDefault();
            }
            e.returnValue = false;
        }

        function move(e) {
            if (dragging) {
                // Mouseup happened outside of window
                if (IE && doc.documentMode < 9 && !e.button) {
                    return stop();
                }

                var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];
                var pageX = t0 && t0.pageX || e.pageX;
                var pageY = t0 && t0.pageY || e.pageY;

                var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
                var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));

                if (hasTouch) {
                    // Stop scrolling in iOS
                    prevent(e);
                }

                onmove.apply(element, [dragX, dragY, e]);
            }
        }

        function start(e) {
            var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);

            if (!rightclick && !dragging) {
                if (onstart.apply(element, arguments) !== false) {
                    dragging = true;
                    maxHeight = $(element).height();
                    maxWidth = $(element).width();
                    offset = $(element).offset();

                    $(doc).on(duringDragEvents);
                    $(doc.body).addClass("sp-dragging");

                    move(e);

                    prevent(e);
                }
            }
        }

        function stop() {
            if (dragging) {
                $(doc).off(duringDragEvents);
                $(doc.body).removeClass("sp-dragging");

                // Wait a tick before notifying observers to allow the click event
                // to fire in Chrome.
                setTimeout(function() {
                    onstop.apply(element, arguments);
                }, 0);
            }
            dragging = false;
        }

        $(element).on("touchstart mousedown", start);
    }

    function throttle(func, wait, debounce) {
        var timeout;
        return function () {
            var context = this, args = arguments;
            var throttler = function () {
                timeout = null;
                func.apply(context, args);
            };
            if (debounce) clearTimeout(timeout);
            if (debounce || !timeout) timeout = setTimeout(throttler, wait);
        };
    }

    function inputTypeColorSupport() {
        return $.fn.spectrum.inputTypeColorSupport();
    }

    /**
     * Define a jQuery plugin
     */
    var dataID = "spectrum.id";
    $.fn.spectrum = function (opts, extra) {

        if (typeof opts == "string") {

            var returnValue = this;
            var args = Array.prototype.slice.call( arguments, 1 );

            this.each(function () {
                var spect = spectrums[$(this).data(dataID)];
                if (spect) {
                    var method = spect[opts];
                    if (!method) {
                        throw new Error( "Spectrum: no such method: '" + opts + "'" );
                    }

                    if (opts == "get") {
                        returnValue = spect.get();
                    }
                    else if (opts == "container") {
                        returnValue = spect.container;
                    }
                    else if (opts == "option") {
                        returnValue = spect.option.apply(spect, args);
                    }
                    else if (opts == "destroy") {
                        spect.destroy();
                        $(this).removeData(dataID);
                    }
                    else {
                        method.apply(spect, args);
                    }
                }
            });

            return returnValue;
        }

        // Initializing a new instance of spectrum
        return this.spectrum("destroy").each(function () {
            var options = $.extend({}, $(this).data(), opts);
            var spect = spectrum(this, options);
            $(this).data(dataID, spect.id);
        });
    };

    $.fn.spectrum.load = true;
    $.fn.spectrum.loadOpts = {};
    $.fn.spectrum.draggable = draggable;
    $.fn.spectrum.defaults = defaultOpts;
    $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {
        if (typeof inputTypeColorSupport._cachedResult === "undefined") {
            var colorInput = $("<input type='color'/>")[0]; // if color element is supported, value will default to not null
            inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== "";
        }
        return inputTypeColorSupport._cachedResult;
    };

    $.spectrum = { };
    $.spectrum.localization = { };
    $.spectrum.palettes = { };

    $.fn.spectrum.processNativeColorInputs = function () {
        var colorInputs = $("input[type=color]");
        if (colorInputs.length && !inputTypeColorSupport()) {
            colorInputs.spectrum({
                preferredFormat: "hex6"
            });
        }
    };

    // TinyColor v1.1.2
    // https://github.com/bgrins/TinyColor
    // Brian Grinstead, MIT License

    (function() {

        var trimLeft = /^[\s,#]+/,
            trimRight = /\s+$/,
            tinyCounter = 0,
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathMax = math.max,
            mathRandom = math.random;

        var tinycolor = function(color, opts) {

            color = (color) ? color : '';
            opts = opts || { };

            // If input is already a tinycolor, return itself
            if (color instanceof tinycolor) {
                return color;
            }
            // If we are called as a function, call using new instead
            if (!(this instanceof tinycolor)) {
                return new tinycolor(color, opts);
            }

            var rgb = inputToRGB(color);
            this._originalInput = color;
            this._r = rgb.r;
            this._g = rgb.g;
            this._b = rgb.b;
            this._a = rgb.a;
            this._roundA = mathRound(1000 * this._a) / 1000;
            this._format = opts.format || rgb.format;
            this._gradientType = opts.gradientType;

            // Don't let the range of [0,255] come back in [0,1].
            // Potentially lose a little bit of precision here, but will fix issues where
            // .5 gets interpreted as half of the total, instead of half of 1
            // If it was supposed to be 128, this was already taken care of by `inputToRgb`
            if (this._r < 1) { this._r = mathRound(this._r); }
            if (this._g < 1) { this._g = mathRound(this._g); }
            if (this._b < 1) { this._b = mathRound(this._b); }

            this._ok = rgb.ok;
            this._tc_id = tinyCounter++;
        };

        tinycolor.prototype = {
            isDark: function() {
                return this.getBrightness() < 128;
            },
            isLight: function() {
                return !this.isDark();
            },
            isValid: function() {
                return this._ok;
            },
            getOriginalInput: function() {
                return this._originalInput;
            },
            getFormat: function() {
                return this._format;
            },
            getAlpha: function() {
                return this._a;
            },
            getBrightness: function() {
                var rgb = this.toRgb();
                return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
            },
            setAlpha: function(value) {
                this._a = boundAlpha(value);
                this._roundA = mathRound(1000 * this._a) / 1000;
                return this;
            },
            toHsv: function() {
                var hsv = rgbToHsv(this._r, this._g, this._b);
                return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
            },
            toHsvString: function() {
                var hsv = rgbToHsv(this._r, this._g, this._b);
                var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
                return (this._a == 1) ?
                    "hsv("  + h + ", " + s + "%, " + v + "%)" :
                    "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
            },
            toHsl: function() {
                var hsl = rgbToHsl(this._r, this._g, this._b);
                return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
            },
            toHslString: function() {
                var hsl = rgbToHsl(this._r, this._g, this._b);
                var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
                return (this._a == 1) ?
                    "hsl("  + h + ", " + s + "%, " + l + "%)" :
                    "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
            },
            toHex: function(allow3Char) {
                return rgbToHex(this._r, this._g, this._b, allow3Char);
            },
            toHexString: function(allow3Char) {
                return '#' + this.toHex(allow3Char);
            },
            toHex8: function() {
                return rgbaToHex(this._r, this._g, this._b, this._a);
            },
            toHex8String: function() {
                return '#' + this.toHex8();
            },
            toRgb: function() {
                return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
            },
            toRgbString: function() {
                return (this._a == 1) ?
                    "rgb("  + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
                    "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
            },
            toPercentageRgb: function() {
                return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
            },
            toPercentageRgbString: function() {
                return (this._a == 1) ?
                    "rgb("  + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
                    "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
            },
            toName: function() {
                if (this._a === 0) {
                    return "transparent";
                }

                if (this._a < 1) {
                    return false;
                }

                return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
            },
            toFilter: function(secondColor) {
                var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
                var secondHex8String = hex8String;
                var gradientType = this._gradientType ? "GradientType = 1, " : "";

                if (secondColor) {
                    var s = tinycolor(secondColor);
                    secondHex8String = s.toHex8String();
                }

                return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
            },
            toString: function(format) {
                var formatSet = !!format;
                format = format || this._format;

                var formattedString = false;
                var hasAlpha = this._a < 1 && this._a >= 0;
                var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");

                if (needsAlphaFormat) {
                    // Special case for "transparent", all other non-alpha formats
                    // will return rgba when there is transparency.
                    if (format === "name" && this._a === 0) {
                        return this.toName();
                    }
                    return this.toRgbString();
                }
                if (format === "rgb") {
                    formattedString = this.toRgbString();
                }
                if (format === "prgb") {
                    formattedString = this.toPercentageRgbString();
                }
                if (format === "hex" || format === "hex6") {
                    formattedString = this.toHexString();
                }
                if (format === "hex3") {
                    formattedString = this.toHexString(true);
                }
                if (format === "hex8") {
                    formattedString = this.toHex8String();
                }
                if (format === "name") {
                    formattedString = this.toName();
                }
                if (format === "hsl") {
                    formattedString = this.toHslString();
                }
                if (format === "hsv") {
                    formattedString = this.toHsvString();
                }

                return formattedString || this.toHexString();
            },

            _applyModification: function(fn, args) {
                var color = fn.apply(null, [this].concat([].slice.call(args)));
                this._r = color._r;
                this._g = color._g;
                this._b = color._b;
                this.setAlpha(color._a);
                return this;
            },
            lighten: function() {
                return this._applyModification(lighten, arguments);
            },
            brighten: function() {
                return this._applyModification(brighten, arguments);
            },
            darken: function() {
                return this._applyModification(darken, arguments);
            },
            desaturate: function() {
                return this._applyModification(desaturate, arguments);
            },
            saturate: function() {
                return this._applyModification(saturate, arguments);
            },
            greyscale: function() {
                return this._applyModification(greyscale, arguments);
            },
            spin: function() {
                return this._applyModification(spin, arguments);
            },

            _applyCombination: function(fn, args) {
                return fn.apply(null, [this].concat([].slice.call(args)));
            },
            analogous: function() {
                return this._applyCombination(analogous, arguments);
            },
            complement: function() {
                return this._applyCombination(complement, arguments);
            },
            monochromatic: function() {
                return this._applyCombination(monochromatic, arguments);
            },
            splitcomplement: function() {
                return this._applyCombination(splitcomplement, arguments);
            },
            triad: function() {
                return this._applyCombination(triad, arguments);
            },
            tetrad: function() {
                return this._applyCombination(tetrad, arguments);
            }
        };

        // If input is an object, force 1 into "1.0" to handle ratios properly
        // String input requires "1.0" as input, so 1 will be treated as 1
        tinycolor.fromRatio = function(color, opts) {
            if (typeof color == "object") {
                var newColor = {};
                for (var i in color) {
                    if (color.hasOwnProperty(i)) {
                        if (i === "a") {
                            newColor[i] = color[i];
                        }
                        else {
                            newColor[i] = convertToPercentage(color[i]);
                        }
                    }
                }
                color = newColor;
            }

            return tinycolor(color, opts);
        };

        // Given a string or object, convert that input to RGB
        // Possible string inputs:
        //
        //     "red"
        //     "#f00" or "f00"
        //     "#ff0000" or "ff0000"
        //     "#ff000000" or "ff000000"
        //     "rgb 255 0 0" or "rgb (255, 0, 0)"
        //     "rgb 1.0 0 0" or "rgb (1, 0, 0)"
        //     "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
        //     "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
        //     "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
        //     "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
        //     "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
        //
        function inputToRGB(color) {

            var rgb = { r: 0, g: 0, b: 0 };
            var a = 1;
            var ok = false;
            var format = false;

            if (typeof color == "string") {
                color = stringInputToObject(color);
            }

            if (typeof color == "object") {
                if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
                    rgb = rgbToRgb(color.r, color.g, color.b);
                    ok = true;
                    format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
                }
                else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
                    color.s = convertToPercentage(color.s);
                    color.v = convertToPercentage(color.v);
                    rgb = hsvToRgb(color.h, color.s, color.v);
                    ok = true;
                    format = "hsv";
                }
                else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
                    color.s = convertToPercentage(color.s);
                    color.l = convertToPercentage(color.l);
                    rgb = hslToRgb(color.h, color.s, color.l);
                    ok = true;
                    format = "hsl";
                }

                if (color.hasOwnProperty("a")) {
                    a = color.a;
                }
            }

            a = boundAlpha(a);

            return {
                ok: ok,
                format: color.format || format,
                r: mathMin(255, mathMax(rgb.r, 0)),
                g: mathMin(255, mathMax(rgb.g, 0)),
                b: mathMin(255, mathMax(rgb.b, 0)),
                a: a
            };
        }


        // Conversion Functions
        // --------------------

        // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
        // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>

        // `rgbToRgb`
        // Handle bounds / percentage checking to conform to CSS color spec
        // <http://www.w3.org/TR/css3-color/>
        // *Assumes:* r, g, b in [0, 255] or [0, 1]
        // *Returns:* { r, g, b } in [0, 255]
        function rgbToRgb(r, g, b){
            return {
                r: bound01(r, 255) * 255,
                g: bound01(g, 255) * 255,
                b: bound01(b, 255) * 255
            };
        }

        // `rgbToHsl`
        // Converts an RGB color value to HSL.
        // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
        // *Returns:* { h, s, l } in [0,1]
        function rgbToHsl(r, g, b) {

            r = bound01(r, 255);
            g = bound01(g, 255);
            b = bound01(b, 255);

            var max = mathMax(r, g, b), min = mathMin(r, g, b);
            var h, s, l = (max + min) / 2;

            if(max == min) {
                h = s = 0; // achromatic
            }
            else {
                var d = max - min;
                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
                switch(max) {
                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                    case g: h = (b - r) / d + 2; break;
                    case b: h = (r - g) / d + 4; break;
                }

                h /= 6;
            }

            return { h: h, s: s, l: l };
        }

        // `hslToRgb`
        // Converts an HSL color value to RGB.
        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
        // *Returns:* { r, g, b } in the set [0, 255]
        function hslToRgb(h, s, l) {
            var r, g, b;

            h = bound01(h, 360);
            s = bound01(s, 100);
            l = bound01(l, 100);

            function hue2rgb(p, q, t) {
                if(t < 0) t += 1;
                if(t > 1) t -= 1;
                if(t < 1/6) return p + (q - p) * 6 * t;
                if(t < 1/2) return q;
                if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
                return p;
            }

            if(s === 0) {
                r = g = b = l; // achromatic
            }
            else {
                var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
                var p = 2 * l - q;
                r = hue2rgb(p, q, h + 1/3);
                g = hue2rgb(p, q, h);
                b = hue2rgb(p, q, h - 1/3);
            }

            return { r: r * 255, g: g * 255, b: b * 255 };
        }

        // `rgbToHsv`
        // Converts an RGB color value to HSV
        // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
        // *Returns:* { h, s, v } in [0,1]
        function rgbToHsv(r, g, b) {

            r = bound01(r, 255);
            g = bound01(g, 255);
            b = bound01(b, 255);

            var max = mathMax(r, g, b), min = mathMin(r, g, b);
            var h, s, v = max;

            var d = max - min;
            s = max === 0 ? 0 : d / max;

            if(max == min) {
                h = 0; // achromatic
            }
            else {
                switch(max) {
                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                    case g: h = (b - r) / d + 2; break;
                    case b: h = (r - g) / d + 4; break;
                }
                h /= 6;
            }
            return { h: h, s: s, v: v };
        }

        // `hsvToRgb`
        // Converts an HSV color value to RGB.
        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
        // *Returns:* { r, g, b } in the set [0, 255]
        function hsvToRgb(h, s, v) {

            h = bound01(h, 360) * 6;
            s = bound01(s, 100);
            v = bound01(v, 100);

            var i = math.floor(h),
                f = h - i,
                p = v * (1 - s),
                q = v * (1 - f * s),
                t = v * (1 - (1 - f) * s),
                mod = i % 6,
                r = [v, q, p, p, t, v][mod],
                g = [t, v, v, q, p, p][mod],
                b = [p, p, t, v, v, q][mod];

            return { r: r * 255, g: g * 255, b: b * 255 };
        }

        // `rgbToHex`
        // Converts an RGB color to hex
        // Assumes r, g, and b are contained in the set [0, 255]
        // Returns a 3 or 6 character hex
        function rgbToHex(r, g, b, allow3Char) {

            var hex = [
                pad2(mathRound(r).toString(16)),
                pad2(mathRound(g).toString(16)),
                pad2(mathRound(b).toString(16))
            ];

            // Return a 3 character hex if possible
            if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
                return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
            }

            return hex.join("");
        }
        // `rgbaToHex`
        // Converts an RGBA color plus alpha transparency to hex
        // Assumes r, g, b and a are contained in the set [0, 255]
        // Returns an 8 character hex
        function rgbaToHex(r, g, b, a) {

            var hex = [
                pad2(convertDecimalToHex(a)),
                pad2(mathRound(r).toString(16)),
                pad2(mathRound(g).toString(16)),
                pad2(mathRound(b).toString(16))
            ];

            return hex.join("");
        }

        // `equals`
        // Can be called with any tinycolor input
        tinycolor.equals = function (color1, color2) {
            if (!color1 || !color2) { return false; }
            return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
        };
        tinycolor.random = function() {
            return tinycolor.fromRatio({
                r: mathRandom(),
                g: mathRandom(),
                b: mathRandom()
            });
        };


        // Modification Functions
        // ----------------------
        // Thanks to less.js for some of the basics here
        // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>

        function desaturate(color, amount) {
            amount = (amount === 0) ? 0 : (amount || 10);
            var hsl = tinycolor(color).toHsl();
            hsl.s -= amount / 100;
            hsl.s = clamp01(hsl.s);
            return tinycolor(hsl);
        }

        function saturate(color, amount) {
            amount = (amount === 0) ? 0 : (amount || 10);
            var hsl = tinycolor(color).toHsl();
            hsl.s += amount / 100;
            hsl.s = clamp01(hsl.s);
            return tinycolor(hsl);
        }

        function greyscale(color) {
            return tinycolor(color).desaturate(100);
        }

        function lighten (color, amount) {
            amount = (amount === 0) ? 0 : (amount || 10);
            var hsl = tinycolor(color).toHsl();
            hsl.l += amount / 100;
            hsl.l = clamp01(hsl.l);
            return tinycolor(hsl);
        }

        function brighten(color, amount) {
            amount = (amount === 0) ? 0 : (amount || 10);
            var rgb = tinycolor(color).toRgb();
            rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
            rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
            rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
            return tinycolor(rgb);
        }

        function darken (color, amount) {
            amount = (amount === 0) ? 0 : (amount || 10);
            var hsl = tinycolor(color).toHsl();
            hsl.l -= amount / 100;
            hsl.l = clamp01(hsl.l);
            return tinycolor(hsl);
        }

        // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
        // Values outside of this range will be wrapped into this range.
        function spin(color, amount) {
            var hsl = tinycolor(color).toHsl();
            var hue = (mathRound(hsl.h) + amount) % 360;
            hsl.h = hue < 0 ? 360 + hue : hue;
            return tinycolor(hsl);
        }

        // Combination Functions
        // ---------------------
        // Thanks to jQuery xColor for some of the ideas behind these
        // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>

        function complement(color) {
            var hsl = tinycolor(color).toHsl();
            hsl.h = (hsl.h + 180) % 360;
            return tinycolor(hsl);
        }

        function triad(color) {
            var hsl = tinycolor(color).toHsl();
            var h = hsl.h;
            return [
                tinycolor(color),
                tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
                tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
            ];
        }

        function tetrad(color) {
            var hsl = tinycolor(color).toHsl();
            var h = hsl.h;
            return [
                tinycolor(color),
                tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
                tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
                tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
            ];
        }

        function splitcomplement(color) {
            var hsl = tinycolor(color).toHsl();
            var h = hsl.h;
            return [
                tinycolor(color),
                tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
                tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
            ];
        }

        function analogous(color, results, slices) {
            results = results || 6;
            slices = slices || 30;

            var hsl = tinycolor(color).toHsl();
            var part = 360 / slices;
            var ret = [tinycolor(color)];

            for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
                hsl.h = (hsl.h + part) % 360;
                ret.push(tinycolor(hsl));
            }
            return ret;
        }

        function monochromatic(color, results) {
            results = results || 6;
            var hsv = tinycolor(color).toHsv();
            var h = hsv.h, s = hsv.s, v = hsv.v;
            var ret = [];
            var modification = 1 / results;

            while (results--) {
                ret.push(tinycolor({ h: h, s: s, v: v}));
                v = (v + modification) % 1;
            }

            return ret;
        }

        // Utility Functions
        // ---------------------

        tinycolor.mix = function(color1, color2, amount) {
            amount = (amount === 0) ? 0 : (amount || 50);

            var rgb1 = tinycolor(color1).toRgb();
            var rgb2 = tinycolor(color2).toRgb();

            var p = amount / 100;
            var w = p * 2 - 1;
            var a = rgb2.a - rgb1.a;

            var w1;

            if (w * a == -1) {
                w1 = w;
            } else {
                w1 = (w + a) / (1 + w * a);
            }

            w1 = (w1 + 1) / 2;

            var w2 = 1 - w1;

            var rgba = {
                r: rgb2.r * w1 + rgb1.r * w2,
                g: rgb2.g * w1 + rgb1.g * w2,
                b: rgb2.b * w1 + rgb1.b * w2,
                a: rgb2.a * p  + rgb1.a * (1 - p)
            };

            return tinycolor(rgba);
        };


        // Readability Functions
        // ---------------------
        // <http://www.w3.org/TR/AERT#color-contrast>

        // `readability`
        // Analyze the 2 colors and returns an object with the following properties:
        //    `brightness`: difference in brightness between the two colors
        //    `color`: difference in color/hue between the two colors
        tinycolor.readability = function(color1, color2) {
            var c1 = tinycolor(color1);
            var c2 = tinycolor(color2);
            var rgb1 = c1.toRgb();
            var rgb2 = c2.toRgb();
            var brightnessA = c1.getBrightness();
            var brightnessB = c2.getBrightness();
            var colorDiff = (
                Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
                Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
                Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
            );

            return {
                brightness: Math.abs(brightnessA - brightnessB),
                color: colorDiff
            };
        };

        // `readable`
        // http://www.w3.org/TR/AERT#color-contrast
        // Ensure that foreground and background color combinations provide sufficient contrast.
        // *Example*
        //    tinycolor.isReadable("#000", "#111") => false
        tinycolor.isReadable = function(color1, color2) {
            var readability = tinycolor.readability(color1, color2);
            return readability.brightness > 125 && readability.color > 500;
        };

        // `mostReadable`
        // Given a base color and a list of possible foreground or background
        // colors for that base, returns the most readable color.
        // *Example*
        //    tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
        tinycolor.mostReadable = function(baseColor, colorList) {
            var bestColor = null;
            var bestScore = 0;
            var bestIsReadable = false;
            for (var i=0; i < colorList.length; i++) {

                // We normalize both around the "acceptable" breaking point,
                // but rank brightness constrast higher than hue.

                var readability = tinycolor.readability(baseColor, colorList[i]);
                var readable = readability.brightness > 125 && readability.color > 500;
                var score = 3 * (readability.brightness / 125) + (readability.color / 500);

                if ((readable && ! bestIsReadable) ||
                    (readable && bestIsReadable && score > bestScore) ||
                    ((! readable) && (! bestIsReadable) && score > bestScore)) {
                    bestIsReadable = readable;
                    bestScore = score;
                    bestColor = tinycolor(colorList[i]);
                }
            }
            return bestColor;
        };


        // Big List of Colors
        // ------------------
        // <http://www.w3.org/TR/css3-color/#svg-color>
        var names = tinycolor.names = {
            aliceblue: "f0f8ff",
            antiquewhite: "faebd7",
            aqua: "0ff",
            aquamarine: "7fffd4",
            azure: "f0ffff",
            beige: "f5f5dc",
            bisque: "ffe4c4",
            black: "000",
            blanchedalmond: "ffebcd",
            blue: "00f",
            blueviolet: "8a2be2",
            brown: "a52a2a",
            burlywood: "deb887",
            burntsienna: "ea7e5d",
            cadetblue: "5f9ea0",
            chartreuse: "7fff00",
            chocolate: "d2691e",
            coral: "ff7f50",
            cornflowerblue: "6495ed",
            cornsilk: "fff8dc",
            crimson: "dc143c",
            cyan: "0ff",
            darkblue: "00008b",
            darkcyan: "008b8b",
            darkgoldenrod: "b8860b",
            darkgray: "a9a9a9",
            darkgreen: "006400",
            darkgrey: "a9a9a9",
            darkkhaki: "bdb76b",
            darkmagenta: "8b008b",
            darkolivegreen: "556b2f",
            darkorange: "ff8c00",
            darkorchid: "9932cc",
            darkred: "8b0000",
            darksalmon: "e9967a",
            darkseagreen: "8fbc8f",
            darkslateblue: "483d8b",
            darkslategray: "2f4f4f",
            darkslategrey: "2f4f4f",
            darkturquoise: "00ced1",
            darkviolet: "9400d3",
            deeppink: "ff1493",
            deepskyblue: "00bfff",
            dimgray: "696969",
            dimgrey: "696969",
            dodgerblue: "1e90ff",
            firebrick: "b22222",
            floralwhite: "fffaf0",
            forestgreen: "228b22",
            fuchsia: "f0f",
            gainsboro: "dcdcdc",
            ghostwhite: "f8f8ff",
            gold: "ffd700",
            goldenrod: "daa520",
            gray: "808080",
            green: "008000",
            greenyellow: "adff2f",
            grey: "808080",
            honeydew: "f0fff0",
            hotpink: "ff69b4",
            indianred: "cd5c5c",
            indigo: "4b0082",
            ivory: "fffff0",
            khaki: "f0e68c",
            lavender: "e6e6fa",
            lavenderblush: "fff0f5",
            lawngreen: "7cfc00",
            lemonchiffon: "fffacd",
            lightblue: "add8e6",
            lightcoral: "f08080",
            lightcyan: "e0ffff",
            lightgoldenrodyellow: "fafad2",
            lightgray: "d3d3d3",
            lightgreen: "90ee90",
            lightgrey: "d3d3d3",
            lightpink: "ffb6c1",
            lightsalmon: "ffa07a",
            lightseagreen: "20b2aa",
            lightskyblue: "87cefa",
            lightslategray: "789",
            lightslategrey: "789",
            lightsteelblue: "b0c4de",
            lightyellow: "ffffe0",
            lime: "0f0",
            limegreen: "32cd32",
            linen: "faf0e6",
            magenta: "f0f",
            maroon: "800000",
            mediumaquamarine: "66cdaa",
            mediumblue: "0000cd",
            mediumorchid: "ba55d3",
            mediumpurple: "9370db",
            mediumseagreen: "3cb371",
            mediumslateblue: "7b68ee",
            mediumspringgreen: "00fa9a",
            mediumturquoise: "48d1cc",
            mediumvioletred: "c71585",
            midnightblue: "191970",
            mintcream: "f5fffa",
            mistyrose: "ffe4e1",
            moccasin: "ffe4b5",
            navajowhite: "ffdead",
            navy: "000080",
            oldlace: "fdf5e6",
            olive: "808000",
            olivedrab: "6b8e23",
            orange: "ffa500",
            orangered: "ff4500",
            orchid: "da70d6",
            palegoldenrod: "eee8aa",
            palegreen: "98fb98",
            paleturquoise: "afeeee",
            palevioletred: "db7093",
            papayawhip: "ffefd5",
            peachpuff: "ffdab9",
            peru: "cd853f",
            pink: "ffc0cb",
            plum: "dda0dd",
            powderblue: "b0e0e6",
            purple: "800080",
            rebeccapurple: "663399",
            red: "f00",
            rosybrown: "bc8f8f",
            royalblue: "4169e1",
            saddlebrown: "8b4513",
            salmon: "fa8072",
            sandybrown: "f4a460",
            seagreen: "2e8b57",
            seashell: "fff5ee",
            sienna: "a0522d",
            silver: "c0c0c0",
            skyblue: "87ceeb",
            slateblue: "6a5acd",
            slategray: "708090",
            slategrey: "708090",
            snow: "fffafa",
            springgreen: "00ff7f",
            steelblue: "4682b4",
            tan: "d2b48c",
            teal: "008080",
            thistle: "d8bfd8",
            tomato: "ff6347",
            turquoise: "40e0d0",
            violet: "ee82ee",
            wheat: "f5deb3",
            white: "fff",
            whitesmoke: "f5f5f5",
            yellow: "ff0",
            yellowgreen: "9acd32"
        };

        // Make it easy to access colors via `hexNames[hex]`
        var hexNames = tinycolor.hexNames = flip(names);


        // Utilities
        // ---------

        // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
        function flip(o) {
            var flipped = { };
            for (var i in o) {
                if (o.hasOwnProperty(i)) {
                    flipped[o[i]] = i;
                }
            }
            return flipped;
        }

        // Return a valid alpha value [0,1] with all invalid values being set to 1
        function boundAlpha(a) {
            a = parseFloat(a);

            if (isNaN(a) || a < 0 || a > 1) {
                a = 1;
            }

            return a;
        }

        // Take input from [0, n] and return it as [0, 1]
        function bound01(n, max) {
            if (isOnePointZero(n)) { n = "100%"; }

            var processPercent = isPercentage(n);
            n = mathMin(max, mathMax(0, parseFloat(n)));

            // Automatically convert percentage into number
            if (processPercent) {
                n = parseInt(n * max, 10) / 100;
            }

            // Handle floating point rounding errors
            if ((math.abs(n - max) < 0.000001)) {
                return 1;
            }

            // Convert into [0, 1] range if it isn't already
            return (n % max) / parseFloat(max);
        }

        // Force a number between 0 and 1
        function clamp01(val) {
            return mathMin(1, mathMax(0, val));
        }

        // Parse a base-16 hex value into a base-10 integer
        function parseIntFromHex(val) {
            return parseInt(val, 16);
        }

        // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
        // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
        function isOnePointZero(n) {
            return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
        }

        // Check to see if string passed in is a percentage
        function isPercentage(n) {
            return typeof n === "string" && n.indexOf('%') != -1;
        }

        // Force a hex value to have 2 characters
        function pad2(c) {
            return c.length == 1 ? '0' + c : '' + c;
        }

        // Replace a decimal with it's percentage value
        function convertToPercentage(n) {
            if (n <= 1) {
                n = (n * 100) + "%";
            }

            return n;
        }

        // Converts a decimal to a hex value
        function convertDecimalToHex(d) {
            return Math.round(parseFloat(d) * 255).toString(16);
        }
        // Converts a hex value to a decimal
        function convertHexToDecimal(h) {
            return (parseIntFromHex(h) / 255);
        }

        var matchers = (function() {

            // <http://www.w3.org/TR/css3-values/#integers>
            var CSS_INTEGER = "[-\\+]?\\d+%?";

            // <http://www.w3.org/TR/css3-values/#number-value>
            var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";

            // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.
            var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";

            // Actual matching.
            // Parentheses and commas are optional, but not required.
            // Whitespace can take the place of commas or opening paren
            var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
            var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";

            return {
                rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
                rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
                hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
                hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
                hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
                hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
                hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
                hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
                hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
            };
        })();

        // `stringInputToObject`
        // Permissive string parsing.  Take in a number of formats, and output an object
        // based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
        function stringInputToObject(color) {

            color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
            var named = false;
            if (names[color]) {
                color = names[color];
                named = true;
            }
            else if (color == 'transparent') {
                return { r: 0, g: 0, b: 0, a: 0, format: "name" };
            }

            // Try to match string input using regular expressions.
            // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
            // Just return an object and let the conversion functions handle that.
            // This way the result will be the same whether the tinycolor is initialized with string or object.
            var match;
            if ((match = matchers.rgb.exec(color))) {
                return { r: match[1], g: match[2], b: match[3] };
            }
            if ((match = matchers.rgba.exec(color))) {
                return { r: match[1], g: match[2], b: match[3], a: match[4] };
            }
            if ((match = matchers.hsl.exec(color))) {
                return { h: match[1], s: match[2], l: match[3] };
            }
            if ((match = matchers.hsla.exec(color))) {
                return { h: match[1], s: match[2], l: match[3], a: match[4] };
            }
            if ((match = matchers.hsv.exec(color))) {
                return { h: match[1], s: match[2], v: match[3] };
            }
            if ((match = matchers.hsva.exec(color))) {
                return { h: match[1], s: match[2], v: match[3], a: match[4] };
            }
            if ((match = matchers.hex8.exec(color))) {
                return {
                    a: convertHexToDecimal(match[1]),
                    r: parseIntFromHex(match[2]),
                    g: parseIntFromHex(match[3]),
                    b: parseIntFromHex(match[4]),
                    format: named ? "name" : "hex8"
                };
            }
            if ((match = matchers.hex6.exec(color))) {
                return {
                    r: parseIntFromHex(match[1]),
                    g: parseIntFromHex(match[2]),
                    b: parseIntFromHex(match[3]),
                    format: named ? "name" : "hex"
                };
            }
            if ((match = matchers.hex3.exec(color))) {
                return {
                    r: parseIntFromHex(match[1] + '' + match[1]),
                    g: parseIntFromHex(match[2] + '' + match[2]),
                    b: parseIntFromHex(match[3] + '' + match[3]),
                    format: named ? "name" : "hex"
                };
            }

            return false;
        }

        window.tinycolor = tinycolor;
    })();

    $(function () {
        if ($.fn.spectrum.load) {
            $.fn.spectrum.processNativeColorInputs();
        }
    });

});

// TinyColor v1.4.2
// https://github.com/bgrins/TinyColor
// Brian Grinstead, MIT License

(function(Math) {

    var trimLeft = /^\s+/,
        trimRight = /\s+$/,
        tinyCounter = 0,
        mathRound = Math.round,
        mathMin = Math.min,
        mathMax = Math.max,
        mathRandom = Math.random;

    function tinycolor (color, opts) {

        color = (color) ? color : '';
        opts = opts || { };

        // If input is already a tinycolor, return itself
        if (color instanceof tinycolor) {
            return color;
        }
        // If we are called as a function, call using new instead
        if (!(this instanceof tinycolor)) {
            return new tinycolor(color, opts);
        }

        var rgb = inputToRGB(color);
        this._originalInput = color,
            this._r = rgb.r,
            this._g = rgb.g,
            this._b = rgb.b,
            this._a = rgb.a,
            this._roundA = mathRound(100*this._a) / 100,
            this._format = opts.format || rgb.format;
        this._gradientType = opts.gradientType;

        // Don't let the range of [0,255] come back in [0,1].
        // Potentially lose a little bit of precision here, but will fix issues where
        // .5 gets interpreted as half of the total, instead of half of 1
        // If it was supposed to be 128, this was already taken care of by `inputToRgb`
        if (this._r < 1) { this._r = mathRound(this._r); }
        if (this._g < 1) { this._g = mathRound(this._g); }
        if (this._b < 1) { this._b = mathRound(this._b); }

        this._ok = rgb.ok;
        this._tc_id = tinyCounter++;
    }

    tinycolor.prototype = {
        isDark: function() {
            return this.getBrightness() < 128;
        },
        isLight: function() {
            return !this.isDark();
        },
        isValid: function() {
            return this._ok;
        },
        getOriginalInput: function() {
            return this._originalInput;
        },
        getFormat: function() {
            return this._format;
        },
        getAlpha: function() {
            return this._a;
        },
        getBrightness: function() {
            //http://www.w3.org/TR/AERT#color-contrast
            var rgb = this.toRgb();
            return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
        },
        getLuminance: function() {
            //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
            var rgb = this.toRgb();
            var RsRGB, GsRGB, BsRGB, R, G, B;
            RsRGB = rgb.r/255;
            GsRGB = rgb.g/255;
            BsRGB = rgb.b/255;

            if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
            if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
            if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
            return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
        },
        setAlpha: function(value) {
            this._a = boundAlpha(value);
            this._roundA = mathRound(100*this._a) / 100;
            return this;
        },
        toHsv: function() {
            var hsv = rgbToHsv(this._r, this._g, this._b);
            return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
        },
        toHsvString: function() {
            var hsv = rgbToHsv(this._r, this._g, this._b);
            var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
            return (this._a == 1) ?
                "hsv("  + h + ", " + s + "%, " + v + "%)" :
                "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
        },
        toHsl: function() {
            var hsl = rgbToHsl(this._r, this._g, this._b);
            return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
        },
        toHslString: function() {
            var hsl = rgbToHsl(this._r, this._g, this._b);
            var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
            return (this._a == 1) ?
                "hsl("  + h + ", " + s + "%, " + l + "%)" :
                "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
        },
        toHex: function(allow3Char) {
            return rgbToHex(this._r, this._g, this._b, allow3Char);
        },
        toHexString: function(allow3Char) {
            return '#' + this.toHex(allow3Char);
        },
        toHex8: function(allow4Char) {
            return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
        },
        toHex8String: function(allow4Char) {
            return '#' + this.toHex8(allow4Char);
        },
        toRgb: function() {
            return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
        },
        toRgbString: function() {
            return (this._a == 1) ?
                "rgb("  + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
                "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
        },
        toPercentageRgb: function() {
            return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
        },
        toPercentageRgbString: function() {
            return (this._a == 1) ?
                "rgb("  + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
                "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
        },
        toName: function() {
            if (this._a === 0) {
                return "transparent";
            }

            if (this._a < 1) {
                return false;
            }

            return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
        },
        toFilter: function(secondColor) {
            var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
            var secondHex8String = hex8String;
            var gradientType = this._gradientType ? "GradientType = 1, " : "";

            if (secondColor) {
                var s = tinycolor(secondColor);
                secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
            }

            return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
        },
        toString: function(format) {
            var formatSet = !!format;
            format = format || this._format;

            var formattedString = false;
            var hasAlpha = this._a < 1 && this._a >= 0;
            var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");

            if (needsAlphaFormat) {
                // Special case for "transparent", all other non-alpha formats
                // will return rgba when there is transparency.
                if (format === "name" && this._a === 0) {
                    return this.toName();
                }
                return this.toRgbString();
            }
            if (format === "rgb") {
                formattedString = this.toRgbString();
            }
            if (format === "prgb") {
                formattedString = this.toPercentageRgbString();
            }
            if (format === "hex" || format === "hex6") {
                formattedString = this.toHexString();
            }
            if (format === "hex3") {
                formattedString = this.toHexString(true);
            }
            if (format === "hex4") {
                formattedString = this.toHex8String(true);
            }
            if (format === "hex8") {
                formattedString = this.toHex8String();
            }
            if (format === "name") {
                formattedString = this.toName();
            }
            if (format === "hsl") {
                formattedString = this.toHslString();
            }
            if (format === "hsv") {
                formattedString = this.toHsvString();
            }

            return formattedString || this.toHexString();
        },
        clone: function() {
            return tinycolor(this.toString());
        },

        _applyModification: function(fn, args) {
            var color = fn.apply(null, [this].concat([].slice.call(args)));
            this._r = color._r;
            this._g = color._g;
            this._b = color._b;
            this.setAlpha(color._a);
            return this;
        },
        lighten: function() {
            return this._applyModification(lighten, arguments);
        },
        brighten: function() {
            return this._applyModification(brighten, arguments);
        },
        darken: function() {
            return this._applyModification(darken, arguments);
        },
        desaturate: function() {
            return this._applyModification(desaturate, arguments);
        },
        saturate: function() {
            return this._applyModification(saturate, arguments);
        },
        greyscale: function() {
            return this._applyModification(greyscale, arguments);
        },
        spin: function() {
            return this._applyModification(spin, arguments);
        },

        _applyCombination: function(fn, args) {
            return fn.apply(null, [this].concat([].slice.call(args)));
        },
        analogous: function() {
            return this._applyCombination(analogous, arguments);
        },
        complement: function() {
            return this._applyCombination(complement, arguments);
        },
        monochromatic: function() {
            return this._applyCombination(monochromatic, arguments);
        },
        splitcomplement: function() {
            return this._applyCombination(splitcomplement, arguments);
        },
        triad: function() {
            return this._applyCombination(triad, arguments);
        },
        tetrad: function() {
            return this._applyCombination(tetrad, arguments);
        }
    };

// If input is an object, force 1 into "1.0" to handle ratios properly
// String input requires "1.0" as input, so 1 will be treated as 1
    tinycolor.fromRatio = function(color, opts) {
        if (typeof color == "object") {
            var newColor = {};
            for (var i in color) {
                if (color.hasOwnProperty(i)) {
                    if (i === "a") {
                        newColor[i] = color[i];
                    }
                    else {
                        newColor[i] = convertToPercentage(color[i]);
                    }
                }
            }
            color = newColor;
        }

        return tinycolor(color, opts);
    };

// Given a string or object, convert that input to RGB
// Possible string inputs:
//
//     "red"
//     "#f00" or "f00"
//     "#ff0000" or "ff0000"
//     "#ff000000" or "ff000000"
//     "rgb 255 0 0" or "rgb (255, 0, 0)"
//     "rgb 1.0 0 0" or "rgb (1, 0, 0)"
//     "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
//     "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
//     "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
//     "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
//     "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
//
    function inputToRGB(color) {

        var rgb = { r: 0, g: 0, b: 0 };
        var a = 1;
        var s = null;
        var v = null;
        var l = null;
        var ok = false;
        var format = false;

        if (typeof color == "string") {
            color = stringInputToObject(color);
        }

        if (typeof color == "object") {
            if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
                rgb = rgbToRgb(color.r, color.g, color.b);
                ok = true;
                format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
            }
            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
                s = convertToPercentage(color.s);
                v = convertToPercentage(color.v);
                rgb = hsvToRgb(color.h, s, v);
                ok = true;
                format = "hsv";
            }
            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
                s = convertToPercentage(color.s);
                l = convertToPercentage(color.l);
                rgb = hslToRgb(color.h, s, l);
                ok = true;
                format = "hsl";
            }

            if (color.hasOwnProperty("a")) {
                a = color.a;
            }
        }

        a = boundAlpha(a);

        return {
            ok: ok,
            format: color.format || format,
            r: mathMin(255, mathMax(rgb.r, 0)),
            g: mathMin(255, mathMax(rgb.g, 0)),
            b: mathMin(255, mathMax(rgb.b, 0)),
            a: a
        };
    }


// Conversion Functions
// --------------------

// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>

// `rgbToRgb`
// Handle bounds / percentage checking to conform to CSS color spec
// <http://www.w3.org/TR/css3-color/>
// *Assumes:* r, g, b in [0, 255] or [0, 1]
// *Returns:* { r, g, b } in [0, 255]
    function rgbToRgb(r, g, b){
        return {
            r: bound01(r, 255) * 255,
            g: bound01(g, 255) * 255,
            b: bound01(b, 255) * 255
        };
    }

// `rgbToHsl`
// Converts an RGB color value to HSL.
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
// *Returns:* { h, s, l } in [0,1]
    function rgbToHsl(r, g, b) {

        r = bound01(r, 255);
        g = bound01(g, 255);
        b = bound01(b, 255);

        var max = mathMax(r, g, b), min = mathMin(r, g, b);
        var h, s, l = (max + min) / 2;

        if(max == min) {
            h = s = 0; // achromatic
        }
        else {
            var d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch(max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }

            h /= 6;
        }

        return { h: h, s: s, l: l };
    }

// `hslToRgb`
// Converts an HSL color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
    function hslToRgb(h, s, l) {
        var r, g, b;

        h = bound01(h, 360);
        s = bound01(s, 100);
        l = bound01(l, 100);

        function hue2rgb(p, q, t) {
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        }

        if(s === 0) {
            r = g = b = l; // achromatic
        }
        else {
            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            var p = 2 * l - q;
            r = hue2rgb(p, q, h + 1/3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1/3);
        }

        return { r: r * 255, g: g * 255, b: b * 255 };
    }

// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
    function rgbToHsv(r, g, b) {

        r = bound01(r, 255);
        g = bound01(g, 255);
        b = bound01(b, 255);

        var max = mathMax(r, g, b), min = mathMin(r, g, b);
        var h, s, v = max;

        var d = max - min;
        s = max === 0 ? 0 : d / max;

        if(max == min) {
            h = 0; // achromatic
        }
        else {
            switch(max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return { h: h, s: s, v: v };
    }

// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
    function hsvToRgb(h, s, v) {

        h = bound01(h, 360) * 6;
        s = bound01(s, 100);
        v = bound01(v, 100);

        var i = Math.floor(h),
            f = h - i,
            p = v * (1 - s),
            q = v * (1 - f * s),
            t = v * (1 - (1 - f) * s),
            mod = i % 6,
            r = [v, q, p, p, t, v][mod],
            g = [t, v, v, q, p, p][mod],
            b = [p, p, t, v, v, q][mod];

        return { r: r * 255, g: g * 255, b: b * 255 };
    }

// `rgbToHex`
// Converts an RGB color to hex
// Assumes r, g, and b are contained in the set [0, 255]
// Returns a 3 or 6 character hex
    function rgbToHex(r, g, b, allow3Char) {

        var hex = [
            pad2(mathRound(r).toString(16)),
            pad2(mathRound(g).toString(16)),
            pad2(mathRound(b).toString(16))
        ];

        // Return a 3 character hex if possible
        if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
        }

        return hex.join("");
    }

// `rgbaToHex`
// Converts an RGBA color plus alpha transparency to hex
// Assumes r, g, b are contained in the set [0, 255] and
// a in [0, 1]. Returns a 4 or 8 character rgba hex
    function rgbaToHex(r, g, b, a, allow4Char) {

        var hex = [
            pad2(mathRound(r).toString(16)),
            pad2(mathRound(g).toString(16)),
            pad2(mathRound(b).toString(16)),
            pad2(convertDecimalToHex(a))
        ];

        // Return a 4 character hex if possible
        if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {
            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
        }

        return hex.join("");
    }

// `rgbaToArgbHex`
// Converts an RGBA color to an ARGB Hex8 string
// Rarely used, but required for "toFilter()"
    function rgbaToArgbHex(r, g, b, a) {

        var hex = [
            pad2(convertDecimalToHex(a)),
            pad2(mathRound(r).toString(16)),
            pad2(mathRound(g).toString(16)),
            pad2(mathRound(b).toString(16))
        ];

        return hex.join("");
    }

// `equals`
// Can be called with any tinycolor input
    tinycolor.equals = function (color1, color2) {
        if (!color1 || !color2) { return false; }
        return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
    };

    tinycolor.random = function() {
        return tinycolor.fromRatio({
            r: mathRandom(),
            g: mathRandom(),
            b: mathRandom()
        });
    };


// Modification Functions
// ----------------------
// Thanks to less.js for some of the basics here
// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>

    function desaturate(color, amount) {
        amount = (amount === 0) ? 0 : (amount || 10);
        var hsl = tinycolor(color).toHsl();
        hsl.s -= amount / 100;
        hsl.s = clamp01(hsl.s);
        return tinycolor(hsl);
    }

    function saturate(color, amount) {
        amount = (amount === 0) ? 0 : (amount || 10);
        var hsl = tinycolor(color).toHsl();
        hsl.s += amount / 100;
        hsl.s = clamp01(hsl.s);
        return tinycolor(hsl);
    }

    function greyscale(color) {
        return tinycolor(color).desaturate(100);
    }

    function lighten (color, amount) {
        amount = (amount === 0) ? 0 : (amount || 10);
        var hsl = tinycolor(color).toHsl();
        hsl.l += amount / 100;
        hsl.l = clamp01(hsl.l);
        return tinycolor(hsl);
    }

    function brighten(color, amount) {
        amount = (amount === 0) ? 0 : (amount || 10);
        var rgb = tinycolor(color).toRgb();
        rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
        rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
        rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
        return tinycolor(rgb);
    }

    function darken (color, amount) {
        amount = (amount === 0) ? 0 : (amount || 10);
        var hsl = tinycolor(color).toHsl();
        hsl.l -= amount / 100;
        hsl.l = clamp01(hsl.l);
        return tinycolor(hsl);
    }

// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
// Values outside of this range will be wrapped into this range.
    function spin(color, amount) {
        var hsl = tinycolor(color).toHsl();
        var hue = (hsl.h + amount) % 360;
        hsl.h = hue < 0 ? 360 + hue : hue;
        return tinycolor(hsl);
    }

// Combination Functions
// ---------------------
// Thanks to jQuery xColor for some of the ideas behind these
// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>

    function complement(color) {
        var hsl = tinycolor(color).toHsl();
        hsl.h = (hsl.h + 180) % 360;
        return tinycolor(hsl);
    }

    function triad(color) {
        var hsl = tinycolor(color).toHsl();
        var h = hsl.h;
        return [
            tinycolor(color),
            tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
            tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
        ];
    }

    function tetrad(color) {
        var hsl = tinycolor(color).toHsl();
        var h = hsl.h;
        return [
            tinycolor(color),
            tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
            tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
            tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
        ];
    }

    function splitcomplement(color) {
        var hsl = tinycolor(color).toHsl();
        var h = hsl.h;
        return [
            tinycolor(color),
            tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
            tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
        ];
    }

    function analogous(color, results, slices) {
        results = results || 6;
        slices = slices || 30;

        var hsl = tinycolor(color).toHsl();
        var part = 360 / slices;
        var ret = [tinycolor(color)];

        for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
            hsl.h = (hsl.h + part) % 360;
            ret.push(tinycolor(hsl));
        }
        return ret;
    }

    function monochromatic(color, results) {
        results = results || 6;
        var hsv = tinycolor(color).toHsv();
        var h = hsv.h, s = hsv.s, v = hsv.v;
        var ret = [];
        var modification = 1 / results;

        while (results--) {
            ret.push(tinycolor({ h: h, s: s, v: v}));
            v = (v + modification) % 1;
        }

        return ret;
    }

// Utility Functions
// ---------------------

    tinycolor.mix = function(color1, color2, amount) {
        amount = (amount === 0) ? 0 : (amount || 50);

        var rgb1 = tinycolor(color1).toRgb();
        var rgb2 = tinycolor(color2).toRgb();

        var p = amount / 100;

        var rgba = {
            r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
            g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
            b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
            a: ((rgb2.a - rgb1.a) * p) + rgb1.a
        };

        return tinycolor(rgba);
    };


// Readability Functions
// ---------------------
// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)

// `contrast`
// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
    tinycolor.readability = function(color1, color2) {
        var c1 = tinycolor(color1);
        var c2 = tinycolor(color2);
        return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
    };

// `isReadable`
// Ensure that foreground and background color combinations meet WCAG2 guidelines.
// The third argument is an optional Object.
//      the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
//      the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.

// *Example*
//    tinycolor.isReadable("#000", "#111") => false
//    tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
    tinycolor.isReadable = function(color1, color2, wcag2) {
        var readability = tinycolor.readability(color1, color2);
        var wcag2Parms, out;

        out = false;

        wcag2Parms = validateWCAG2Parms(wcag2);
        switch (wcag2Parms.level + wcag2Parms.size) {
            case "AAsmall":
            case "AAAlarge":
                out = readability >= 4.5;
                break;
            case "AAlarge":
                out = readability >= 3;
                break;
            case "AAAsmall":
                out = readability >= 7;
                break;
        }
        return out;

    };

// `mostReadable`
// Given a base color and a list of possible foreground or background
// colors for that base, returns the most readable color.
// Optionally returns Black or White if the most readable color is unreadable.
// *Example*
//    tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
//    tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString();  // "#ffffff"
//    tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
//    tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
    tinycolor.mostReadable = function(baseColor, colorList, args) {
        var bestColor = null;
        var bestScore = 0;
        var readability;
        var includeFallbackColors, level, size ;
        args = args || {};
        includeFallbackColors = args.includeFallbackColors ;
        level = args.level;
        size = args.size;

        for (var i= 0; i < colorList.length ; i++) {
            readability = tinycolor.readability(baseColor, colorList[i]);
            if (readability > bestScore) {
                bestScore = readability;
                bestColor = tinycolor(colorList[i]);
            }
        }

        if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
            return bestColor;
        }
        else {
            args.includeFallbackColors=false;
            return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
        }
    };


// Big List of Colors
// ------------------
// <http://www.w3.org/TR/css3-color/#svg-color>
    var names = tinycolor.names = {
        aliceblue: "f0f8ff",
        antiquewhite: "faebd7",
        aqua: "0ff",
        aquamarine: "7fffd4",
        azure: "f0ffff",
        beige: "f5f5dc",
        bisque: "ffe4c4",
        black: "000",
        blanchedalmond: "ffebcd",
        blue: "00f",
        blueviolet: "8a2be2",
        brown: "a52a2a",
        burlywood: "deb887",
        burntsienna: "ea7e5d",
        cadetblue: "5f9ea0",
        chartreuse: "7fff00",
        chocolate: "d2691e",
        coral: "ff7f50",
        cornflowerblue: "6495ed",
        cornsilk: "fff8dc",
        crimson: "dc143c",
        cyan: "0ff",
        darkblue: "00008b",
        darkcyan: "008b8b",
        darkgoldenrod: "b8860b",
        darkgray: "a9a9a9",
        darkgreen: "006400",
        darkgrey: "a9a9a9",
        darkkhaki: "bdb76b",
        darkmagenta: "8b008b",
        darkolivegreen: "556b2f",
        darkorange: "ff8c00",
        darkorchid: "9932cc",
        darkred: "8b0000",
        darksalmon: "e9967a",
        darkseagreen: "8fbc8f",
        darkslateblue: "483d8b",
        darkslategray: "2f4f4f",
        darkslategrey: "2f4f4f",
        darkturquoise: "00ced1",
        darkviolet: "9400d3",
        deeppink: "ff1493",
        deepskyblue: "00bfff",
        dimgray: "696969",
        dimgrey: "696969",
        dodgerblue: "1e90ff",
        firebrick: "b22222",
        floralwhite: "fffaf0",
        forestgreen: "228b22",
        fuchsia: "f0f",
        gainsboro: "dcdcdc",
        ghostwhite: "f8f8ff",
        gold: "ffd700",
        goldenrod: "daa520",
        gray: "808080",
        green: "008000",
        greenyellow: "adff2f",
        grey: "808080",
        honeydew: "f0fff0",
        hotpink: "ff69b4",
        indianred: "cd5c5c",
        indigo: "4b0082",
        ivory: "fffff0",
        khaki: "f0e68c",
        lavender: "e6e6fa",
        lavenderblush: "fff0f5",
        lawngreen: "7cfc00",
        lemonchiffon: "fffacd",
        lightblue: "add8e6",
        lightcoral: "f08080",
        lightcyan: "e0ffff",
        lightgoldenrodyellow: "fafad2",
        lightgray: "d3d3d3",
        lightgreen: "90ee90",
        lightgrey: "d3d3d3",
        lightpink: "ffb6c1",
        lightsalmon: "ffa07a",
        lightseagreen: "20b2aa",
        lightskyblue: "87cefa",
        lightslategray: "789",
        lightslategrey: "789",
        lightsteelblue: "b0c4de",
        lightyellow: "ffffe0",
        lime: "0f0",
        limegreen: "32cd32",
        linen: "faf0e6",
        magenta: "f0f",
        maroon: "800000",
        mediumaquamarine: "66cdaa",
        mediumblue: "0000cd",
        mediumorchid: "ba55d3",
        mediumpurple: "9370db",
        mediumseagreen: "3cb371",
        mediumslateblue: "7b68ee",
        mediumspringgreen: "00fa9a",
        mediumturquoise: "48d1cc",
        mediumvioletred: "c71585",
        midnightblue: "191970",
        mintcream: "f5fffa",
        mistyrose: "ffe4e1",
        moccasin: "ffe4b5",
        navajowhite: "ffdead",
        navy: "000080",
        oldlace: "fdf5e6",
        olive: "808000",
        olivedrab: "6b8e23",
        orange: "ffa500",
        orangered: "ff4500",
        orchid: "da70d6",
        palegoldenrod: "eee8aa",
        palegreen: "98fb98",
        paleturquoise: "afeeee",
        palevioletred: "db7093",
        papayawhip: "ffefd5",
        peachpuff: "ffdab9",
        peru: "cd853f",
        pink: "ffc0cb",
        plum: "dda0dd",
        powderblue: "b0e0e6",
        purple: "800080",
        rebeccapurple: "663399",
        red: "f00",
        rosybrown: "bc8f8f",
        royalblue: "4169e1",
        saddlebrown: "8b4513",
        salmon: "fa8072",
        sandybrown: "f4a460",
        seagreen: "2e8b57",
        seashell: "fff5ee",
        sienna: "a0522d",
        silver: "c0c0c0",
        skyblue: "87ceeb",
        slateblue: "6a5acd",
        slategray: "708090",
        slategrey: "708090",
        snow: "fffafa",
        springgreen: "00ff7f",
        steelblue: "4682b4",
        tan: "d2b48c",
        teal: "008080",
        thistle: "d8bfd8",
        tomato: "ff6347",
        turquoise: "40e0d0",
        violet: "ee82ee",
        wheat: "f5deb3",
        white: "fff",
        whitesmoke: "f5f5f5",
        yellow: "ff0",
        yellowgreen: "9acd32"
    };

// Make it easy to access colors via `hexNames[hex]`
    var hexNames = tinycolor.hexNames = flip(names);


// Utilities
// ---------

// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
    function flip(o) {
        var flipped = { };
        for (var i in o) {
            if (o.hasOwnProperty(i)) {
                flipped[o[i]] = i;
            }
        }
        return flipped;
    }

// Return a valid alpha value [0,1] with all invalid values being set to 1
    function boundAlpha(a) {
        a = parseFloat(a);

        if (isNaN(a) || a < 0 || a > 1) {
            a = 1;
        }

        return a;
    }

// Take input from [0, n] and return it as [0, 1]
    function bound01(n, max) {
        if (isOnePointZero(n)) { n = "100%"; }

        var processPercent = isPercentage(n);
        n = mathMin(max, mathMax(0, parseFloat(n)));

        // Automatically convert percentage into number
        if (processPercent) {
            n = parseInt(n * max, 10) / 100;
        }

        // Handle floating point rounding errors
        if ((Math.abs(n - max) < 0.000001)) {
            return 1;
        }

        // Convert into [0, 1] range if it isn't already
        return (n % max) / parseFloat(max);
    }

// Force a number between 0 and 1
    function clamp01(val) {
        return mathMin(1, mathMax(0, val));
    }

// Parse a base-16 hex value into a base-10 integer
    function parseIntFromHex(val) {
        return parseInt(val, 16);
    }

// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
    function isOnePointZero(n) {
        return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
    }

// Check to see if string passed in is a percentage
    function isPercentage(n) {
        return typeof n === "string" && n.indexOf('%') != -1;
    }

// Force a hex value to have 2 characters
    function pad2(c) {
        return c.length == 1 ? '0' + c : '' + c;
    }

// Replace a decimal with it's percentage value
    function convertToPercentage(n) {
        if (n <= 1) {
            n = (n * 100) + "%";
        }

        return n;
    }

// Converts a decimal to a hex value
    function convertDecimalToHex(d) {
        return Math.round(parseFloat(d) * 255).toString(16);
    }
// Converts a hex value to a decimal
    function convertHexToDecimal(h) {
        return (parseIntFromHex(h) / 255);
    }

    var matchers = (function() {

        // <http://www.w3.org/TR/css3-values/#integers>
        var CSS_INTEGER = "[-\\+]?\\d+%?";

        // <http://www.w3.org/TR/css3-values/#number-value>
        var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";

        // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.
        var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";

        // Actual matching.
        // Parentheses and commas are optional, but not required.
        // Whitespace can take the place of commas or opening paren
        var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
        var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";

        return {
            CSS_UNIT: new RegExp(CSS_UNIT),
            rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
            rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
            hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
            hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
            hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
            hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
            hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
            hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
            hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
            hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
        };
    })();

// `isValidCSSUnit`
// Take in a single string / number and check to see if it looks like a CSS unit
// (see `matchers` above for definition).
    function isValidCSSUnit(color) {
        return !!matchers.CSS_UNIT.exec(color);
    }

// `stringInputToObject`
// Permissive string parsing.  Take in a number of formats, and output an object
// based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
    function stringInputToObject(color) {

        color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
        var named = false;
        if (names[color]) {
            color = names[color];
            named = true;
        }
        else if (color == 'transparent') {
            return { r: 0, g: 0, b: 0, a: 0, format: "name" };
        }

        // Try to match string input using regular expressions.
        // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
        // Just return an object and let the conversion functions handle that.
        // This way the result will be the same whether the tinycolor is initialized with string or object.
        var match;
        if ((match = matchers.rgb.exec(color))) {
            return { r: match[1], g: match[2], b: match[3] };
        }
        if ((match = matchers.rgba.exec(color))) {
            return { r: match[1], g: match[2], b: match[3], a: match[4] };
        }
        if ((match = matchers.hsl.exec(color))) {
            return { h: match[1], s: match[2], l: match[3] };
        }
        if ((match = matchers.hsla.exec(color))) {
            return { h: match[1], s: match[2], l: match[3], a: match[4] };
        }
        if ((match = matchers.hsv.exec(color))) {
            return { h: match[1], s: match[2], v: match[3] };
        }
        if ((match = matchers.hsva.exec(color))) {
            return { h: match[1], s: match[2], v: match[3], a: match[4] };
        }
        if ((match = matchers.hex8.exec(color))) {
            return {
                r: parseIntFromHex(match[1]),
                g: parseIntFromHex(match[2]),
                b: parseIntFromHex(match[3]),
                a: convertHexToDecimal(match[4]),
                format: named ? "name" : "hex8"
            };
        }
        if ((match = matchers.hex6.exec(color))) {
            return {
                r: parseIntFromHex(match[1]),
                g: parseIntFromHex(match[2]),
                b: parseIntFromHex(match[3]),
                format: named ? "name" : "hex"
            };
        }
        if ((match = matchers.hex4.exec(color))) {
            return {
                r: parseIntFromHex(match[1] + '' + match[1]),
                g: parseIntFromHex(match[2] + '' + match[2]),
                b: parseIntFromHex(match[3] + '' + match[3]),
                a: convertHexToDecimal(match[4] + '' + match[4]),
                format: named ? "name" : "hex8"
            };
        }
        if ((match = matchers.hex3.exec(color))) {
            return {
                r: parseIntFromHex(match[1] + '' + match[1]),
                g: parseIntFromHex(match[2] + '' + match[2]),
                b: parseIntFromHex(match[3] + '' + match[3]),
                format: named ? "name" : "hex"
            };
        }

        return false;
    }

    function validateWCAG2Parms(parms) {
        // return valid WCAG2 parms for isReadable.
        // If input parms are invalid, return {"level":"AA", "size":"small"}
        var level, size;
        parms = parms || {"level":"AA", "size":"small"};
        level = (parms.level || "AA").toUpperCase();
        size = (parms.size || "small").toLowerCase();
        if (level !== "AA" && level !== "AAA") {
            level = "AA";
        }
        if (size !== "small" && size !== "large") {
            size = "small";
        }
        return {"level":level, "size":size};
    }

// Node: Export function
    if (typeof module !== "undefined" && module.exports) {
        module.exports = tinycolor;
    }
// AMD/requirejs: Define the module
    else if (typeof define === 'function' && define.amd) {
        define('tinycolor',[],function () {return tinycolor;});
    }
// Browser: Expose to window
    else {
        window.tinycolor = tinycolor;
    }

})(Math);

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Ui/js/lib/knockout/bindings/color-picker',[
    'ko',
    'jquery',
    '../template/renderer',
    'spectrum',
    'tinycolor'
], function (ko, $, renderer, spectrum, tinycolor) {
    'use strict';

    /**
     * Change color picker status to be enabled or disabled
     *
     * @param {HTMLElement} element - Element to apply colorpicker enable/disable status to.
     * @param {Object} viewModel - Object, which represents view model binded to el.
     */
    function changeColorPickerStateBasedOnViewModel(element, viewModel) {
        $(element).spectrum(viewModel.disabled() ? 'disable' : 'enable');
    }

    ko.bindingHandlers.colorPicker = {
        /**
         * Binding init callback.
         *
         * @param {*} element
         * @param {Function} valueAccessor
         * @param {Function} allBindings
         * @param {Object} viewModel
         */
        init: function (element, valueAccessor, allBindings, viewModel) {
            var config = valueAccessor(),

                /** change value */
                changeValue = function (value) {
                    if (value == null) {
                        value = '';
                    }
                    config.value(value.toString());
                };

            config.change = changeValue;

            config.hide = changeValue;

            /** show value */
            config.show = function () {
                if (!viewModel.focused()) {
                    viewModel.focused(true);
                }

                return true;
            };

            $(element).spectrum(config);

            changeColorPickerStateBasedOnViewModel(element, viewModel);
        },

        /**
         * Reads params passed to binding, parses component declarations.
         * Fetches for those found and attaches them to the new context.
         *
         * @param {HTMLElement} element - 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 element.
         */
        update: function (element, valueAccessor, allBindings, viewModel) {
            var config = valueAccessor();

            /** Initialise value as empty if it is undefined when color picker input is reset **/
            if (config.value() === undefined) {
                config.value('');
            }

            if (tinycolor(config.value()).isValid() || config.value() === '') {
                $(element).spectrum('set', config.value());

                if (config.value() !== '') {
                    config.value($(element).spectrum('get').toString());
                }
            }

            changeColorPickerStateBasedOnViewModel(element, viewModel);
        }
    };

    renderer.addAttribute('colorPicker');
});

/**
 * 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;
            }
        }
    };
});

/*!
 * 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

( function( factory ) {
    "use strict";

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

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

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

    var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g;

    return $.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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/checkboxradio',[
            "jquery",
            "../form-reset-mixin",
            "../labels",
            "../widget"
        ], factory );
    } else {

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

    $.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 } );
            }
        }

    } ] );

    return $.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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/button',[
            "jquery",

            // These are only for backcompat
            // TODO: Remove after 1.12
            "./controlgroup",
            "./checkboxradio",

            "../keycode",
            "../widget"
        ], factory );
    } else {

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

    $.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 );
        };
    }

    return $.ui.button;

} );

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

//>>label: Spinner
//>>group: Widgets
//>>description: Displays buttons to easily input numbers via the keyboard or mouse.
//>>docs: http://api.jqueryui.com/spinner/
//>>demos: http://jqueryui.com/spinner/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/spinner.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/spinner',[
            "jquery",
            "./button",
            "../version",
            "../keycode",
            "../safe-active-element",
            "../widget"
        ], factory );
    } else {

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

    function spinnerModifier( fn ) {
        return function() {
            var previous = this.element.val();
            fn.apply( this, arguments );
            this._refresh();
            if ( previous !== this.element.val() ) {
                this._trigger( "change" );
            }
        };
    }

    $.widget( "ui.spinner", {
        version: "1.13.2",
        defaultElement: "<input>",
        widgetEventPrefix: "spin",
        options: {
            classes: {
                "ui-spinner": "ui-corner-all",
                "ui-spinner-down": "ui-corner-br",
                "ui-spinner-up": "ui-corner-tr"
            },
            culture: null,
            icons: {
                down: "ui-icon-triangle-1-s",
                up: "ui-icon-triangle-1-n"
            },
            incremental: true,
            max: null,
            min: null,
            numberFormat: null,
            page: 10,
            step: 1,

            change: null,
            spin: null,
            start: null,
            stop: null
        },

        _create: function() {

            // handle string values that need to be parsed
            this._setOption( "max", this.options.max );
            this._setOption( "min", this.options.min );
            this._setOption( "step", this.options.step );

            // Only format if there is a value, prevents the field from being marked
            // as invalid in Firefox, see #9573.
            if ( this.value() !== "" ) {

                // Format the value, but don't constrain.
                this._value( this.element.val(), true );
            }

            this._draw();
            this._on( this._events );
            this._refresh();

            // Turning off autocomplete prevents the browser from remembering the
            // value when navigating through history, so we re-enable autocomplete
            // if the page is unloaded before the widget is destroyed. #7790
            this._on( this.window, {
                beforeunload: function() {
                    this.element.removeAttr( "autocomplete" );
                }
            } );
        },

        _getCreateOptions: function() {
            var options = this._super();
            var element = this.element;

            $.each( [ "min", "max", "step" ], function( i, option ) {
                var value = element.attr( option );
                if ( value != null && value.length ) {
                    options[ option ] = value;
                }
            } );

            return options;
        },

        _events: {
            keydown: function( event ) {
                if ( this._start( event ) && this._keydown( event ) ) {
                    event.preventDefault();
                }
            },
            keyup: "_stop",
            focus: function() {
                this.previous = this.element.val();
            },
            blur: function( event ) {
                if ( this.cancelBlur ) {
                    delete this.cancelBlur;
                    return;
                }

                this._stop();
                this._refresh();
                if ( this.previous !== this.element.val() ) {
                    this._trigger( "change", event );
                }
            },
            mousewheel: function( event, delta ) {
                var activeElement = $.ui.safeActiveElement( this.document[ 0 ] );
                var isActive = this.element[ 0 ] === activeElement;

                if ( !isActive || !delta ) {
                    return;
                }

                if ( !this.spinning && !this._start( event ) ) {
                    return false;
                }

                this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event );
                clearTimeout( this.mousewheelTimer );
                this.mousewheelTimer = this._delay( function() {
                    if ( this.spinning ) {
                        this._stop( event );
                    }
                }, 100 );
                event.preventDefault();
            },
            "mousedown .ui-spinner-button": function( event ) {
                var previous;

                // We never want the buttons to have focus; whenever the user is
                // interacting with the spinner, the focus should be on the input.
                // If the input is focused then this.previous is properly set from
                // when the input first received focus. If the input is not focused
                // then we need to set this.previous based on the value before spinning.
                previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ?
                    this.previous : this.element.val();
                function checkFocus() {
                    var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] );
                    if ( !isActive ) {
                        this.element.trigger( "focus" );
                        this.previous = previous;

                        // support: IE
                        // IE sets focus asynchronously, so we need to check if focus
                        // moved off of the input because the user clicked on the button.
                        this._delay( function() {
                            this.previous = previous;
                        } );
                    }
                }

                // Ensure focus is on (or stays on) the text field
                event.preventDefault();
                checkFocus.call( this );

                // Support: IE
                // IE doesn't prevent moving focus even with event.preventDefault()
                // so we set a flag to know when we should ignore the blur event
                // and check (again) if focus moved off of the input.
                this.cancelBlur = true;
                this._delay( function() {
                    delete this.cancelBlur;
                    checkFocus.call( this );
                } );

                if ( this._start( event ) === false ) {
                    return;
                }

                this._repeat( null, $( event.currentTarget )
                    .hasClass( "ui-spinner-up" ) ? 1 : -1, event );
            },
            "mouseup .ui-spinner-button": "_stop",
            "mouseenter .ui-spinner-button": function( event ) {

                // button will add ui-state-active if mouse was down while mouseleave and kept down
                if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
                    return;
                }

                if ( this._start( event ) === false ) {
                    return false;
                }
                this._repeat( null, $( event.currentTarget )
                    .hasClass( "ui-spinner-up" ) ? 1 : -1, event );
            },

            // TODO: do we really want to consider this a stop?
            // shouldn't we just stop the repeater and wait until mouseup before
            // we trigger the stop event?
            "mouseleave .ui-spinner-button": "_stop"
        },

        // Support mobile enhanced option and make backcompat more sane
        _enhance: function() {
            this.uiSpinner = this.element
                .attr( "autocomplete", "off" )
                .wrap( "<span>" )
                .parent()

                // Add buttons
                .append(
                    "<a></a><a></a>"
                );
        },

        _draw: function() {
            this._enhance();

            this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
            this._addClass( "ui-spinner-input" );

            this.element.attr( "role", "spinbutton" );

            // Button bindings
            this.buttons = this.uiSpinner.children( "a" )
                .attr( "tabIndex", -1 )
                .attr( "aria-hidden", true )
                .button( {
                    classes: {
                        "ui-button": ""
                    }
                } );

            // TODO: Right now button does not support classes this is already updated in button PR
            this._removeClass( this.buttons, "ui-corner-all" );

            this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
            this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
            this.buttons.first().button( {
                "icon": this.options.icons.up,
                "showLabel": false
            } );
            this.buttons.last().button( {
                "icon": this.options.icons.down,
                "showLabel": false
            } );

            // IE 6 doesn't understand height: 50% for the buttons
            // unless the wrapper has an explicit height
            if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) &&
                this.uiSpinner.height() > 0 ) {
                this.uiSpinner.height( this.uiSpinner.height() );
            }
        },

        _keydown: function( event ) {
            var options = this.options,
                keyCode = $.ui.keyCode;

            switch ( event.keyCode ) {
                case keyCode.UP:
                    this._repeat( null, 1, event );
                    return true;
                case keyCode.DOWN:
                    this._repeat( null, -1, event );
                    return true;
                case keyCode.PAGE_UP:
                    this._repeat( null, options.page, event );
                    return true;
                case keyCode.PAGE_DOWN:
                    this._repeat( null, -options.page, event );
                    return true;
            }

            return false;
        },

        _start: function( event ) {
            if ( !this.spinning && this._trigger( "start", event ) === false ) {
                return false;
            }

            if ( !this.counter ) {
                this.counter = 1;
            }
            this.spinning = true;
            return true;
        },

        _repeat: function( i, steps, event ) {
            i = i || 500;

            clearTimeout( this.timer );
            this.timer = this._delay( function() {
                this._repeat( 40, steps, event );
            }, i );

            this._spin( steps * this.options.step, event );
        },

        _spin: function( step, event ) {
            var value = this.value() || 0;

            if ( !this.counter ) {
                this.counter = 1;
            }

            value = this._adjustValue( value + step * this._increment( this.counter ) );

            if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) {
                this._value( value );
                this.counter++;
            }
        },

        _increment: function( i ) {
            var incremental = this.options.incremental;

            if ( incremental ) {
                return typeof incremental === "function" ?
                    incremental( i ) :
                    Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
            }

            return 1;
        },

        _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;
        },

        _adjustValue: function( value ) {
            var base, aboveMin,
                options = this.options;

            // Make sure we're at a valid step
            // - find out where we are relative to the base (min or 0)
            base = options.min !== null ? options.min : 0;
            aboveMin = value - base;

            // - round to the nearest step
            aboveMin = Math.round( aboveMin / options.step ) * options.step;

            // - rounding is based on 0, so adjust back to our base
            value = base + aboveMin;

            // Fix precision from bad JS floating point math
            value = parseFloat( value.toFixed( this._precision() ) );

            // Clamp the value
            if ( options.max !== null && value > options.max ) {
                return options.max;
            }
            if ( options.min !== null && value < options.min ) {
                return options.min;
            }

            return value;
        },

        _stop: function( event ) {
            if ( !this.spinning ) {
                return;
            }

            clearTimeout( this.timer );
            clearTimeout( this.mousewheelTimer );
            this.counter = 0;
            this.spinning = false;
            this._trigger( "stop", event );
        },

        _setOption: function( key, value ) {
            var prevValue, first, last;

            if ( key === "culture" || key === "numberFormat" ) {
                prevValue = this._parse( this.element.val() );
                this.options[ key ] = value;
                this.element.val( this._format( prevValue ) );
                return;
            }

            if ( key === "max" || key === "min" || key === "step" ) {
                if ( typeof value === "string" ) {
                    value = this._parse( value );
                }
            }
            if ( key === "icons" ) {
                first = this.buttons.first().find( ".ui-icon" );
                this._removeClass( first, null, this.options.icons.up );
                this._addClass( first, null, value.up );
                last = this.buttons.last().find( ".ui-icon" );
                this._removeClass( last, null, this.options.icons.down );
                this._addClass( last, null, value.down );
            }

            this._super( key, value );
        },

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

            this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
            this.element.prop( "disabled", !!value );
            this.buttons.button( value ? "disable" : "enable" );
        },

        _setOptions: spinnerModifier( function( options ) {
            this._super( options );
        } ),

        _parse: function( val ) {
            if ( typeof val === "string" && val !== "" ) {
                val = window.Globalize && this.options.numberFormat ?
                    Globalize.parseFloat( val, 10, this.options.culture ) : +val;
            }
            return val === "" || isNaN( val ) ? null : val;
        },

        _format: function( value ) {
            if ( value === "" ) {
                return "";
            }
            return window.Globalize && this.options.numberFormat ?
                Globalize.format( value, this.options.numberFormat, this.options.culture ) :
                value;
        },

        _refresh: function() {
            this.element.attr( {
                "aria-valuemin": this.options.min,
                "aria-valuemax": this.options.max,

                // TODO: what should we do with values that can't be parsed?
                "aria-valuenow": this._parse( this.element.val() )
            } );
        },

        isValid: function() {
            var value = this.value();

            // Null is invalid
            if ( value === null ) {
                return false;
            }

            // If value gets adjusted, it's invalid
            return value === this._adjustValue( value );
        },

        // Update the value without triggering change
        _value: function( value, allowAny ) {
            var parsed;
            if ( value !== "" ) {
                parsed = this._parse( value );
                if ( parsed !== null ) {
                    if ( !allowAny ) {
                        parsed = this._adjustValue( parsed );
                    }
                    value = this._format( parsed );
                }
            }
            this.element.val( value );
            this._refresh();
        },

        _destroy: function() {
            this.element
                .prop( "disabled", false )
                .removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" );

            this.uiSpinner.replaceWith( this.element );
        },

        stepUp: spinnerModifier( function( steps ) {
            this._stepUp( steps );
        } ),
        _stepUp: function( steps ) {
            if ( this._start() ) {
                this._spin( ( steps || 1 ) * this.options.step );
                this._stop();
            }
        },

        stepDown: spinnerModifier( function( steps ) {
            this._stepDown( steps );
        } ),
        _stepDown: function( steps ) {
            if ( this._start() ) {
                this._spin( ( steps || 1 ) * -this.options.step );
                this._stop();
            }
        },

        pageUp: spinnerModifier( function( pages ) {
            this._stepUp( ( pages || 1 ) * this.options.page );
        } ),

        pageDown: spinnerModifier( function( pages ) {
            this._stepDown( ( pages || 1 ) * this.options.page );
        } ),

        value: function( newVal ) {
            if ( !arguments.length ) {
                return this._parse( this.element.val() );
            }
            spinnerModifier( this._value ).call( this, newVal );
        },

        widget: function() {
            return this.uiSpinner;
        }
    } );

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
    if ( $.uiBackCompat !== false ) {

        // Backcompat for spinner html extension points
        $.widget( "ui.spinner", $.ui.spinner, {
            _enhance: function() {
                this.uiSpinner = this.element
                    .attr( "autocomplete", "off" )
                    .wrap( this._uiSpinnerHtml() )
                    .parent()

                    // Add buttons
                    .append( this._buttonHtml() );
            },
            _uiSpinnerHtml: function() {
                return "<span>";
            },

            _buttonHtml: function() {
                return "<a></a><a></a>";
            }
        } );
    }

    return $.ui.spinner;

} );

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

//>>label: Size Effect
//>>group: Effects
//>>description: Resize an element to a specified width and height.
//>>docs: http://api.jqueryui.com/size-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "size", function( options, done ) {

        // Create element
        var baseline, factor, temp,
            element = $( this ),

            // Copy for children
            cProps = [ "fontSize" ],
            vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
            hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],

            // Set options
            mode = options.mode,
            restore = mode !== "effect",
            scale = options.scale || "both",
            origin = options.origin || [ "middle", "center" ],
            position = element.css( "position" ),
            pos = element.position(),
            original = $.effects.scaledDimensions( element ),
            from = options.from || original,
            to = options.to || $.effects.scaledDimensions( element, 0 );

        $.effects.createPlaceholder( element );

        if ( mode === "show" ) {
            temp = from;
            from = to;
            to = temp;
        }

        // Set scaling factor
        factor = {
            from: {
                y: from.height / original.height,
                x: from.width / original.width
            },
            to: {
                y: to.height / original.height,
                x: to.width / original.width
            }
        };

        // Scale the css box
        if ( scale === "box" || scale === "both" ) {

            // Vertical props scaling
            if ( factor.from.y !== factor.to.y ) {
                from = $.effects.setTransition( element, vProps, factor.from.y, from );
                to = $.effects.setTransition( element, vProps, factor.to.y, to );
            }

            // Horizontal props scaling
            if ( factor.from.x !== factor.to.x ) {
                from = $.effects.setTransition( element, hProps, factor.from.x, from );
                to = $.effects.setTransition( element, hProps, factor.to.x, to );
            }
        }

        // Scale the content
        if ( scale === "content" || scale === "both" ) {

            // Vertical props scaling
            if ( factor.from.y !== factor.to.y ) {
                from = $.effects.setTransition( element, cProps, factor.from.y, from );
                to = $.effects.setTransition( element, cProps, factor.to.y, to );
            }
        }

        // Adjust the position properties based on the provided origin points
        if ( origin ) {
            baseline = $.effects.getBaseline( origin, original );
            from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top;
            from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left;
            to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top;
            to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left;
        }
        delete from.outerHeight;
        delete from.outerWidth;
        element.css( from );

        // Animate the children if desired
        if ( scale === "content" || scale === "both" ) {

            vProps = vProps.concat( [ "marginTop", "marginBottom" ] ).concat( cProps );
            hProps = hProps.concat( [ "marginLeft", "marginRight" ] );

            // Only animate children with width attributes specified
            // TODO: is this right? should we include anything with css width specified as well
            element.find( "*[width]" ).each( function() {
                var child = $( this ),
                    childOriginal = $.effects.scaledDimensions( child ),
                    childFrom = {
                        height: childOriginal.height * factor.from.y,
                        width: childOriginal.width * factor.from.x,
                        outerHeight: childOriginal.outerHeight * factor.from.y,
                        outerWidth: childOriginal.outerWidth * factor.from.x
                    },
                    childTo = {
                        height: childOriginal.height * factor.to.y,
                        width: childOriginal.width * factor.to.x,
                        outerHeight: childOriginal.height * factor.to.y,
                        outerWidth: childOriginal.width * factor.to.x
                    };

                // Vertical props scaling
                if ( factor.from.y !== factor.to.y ) {
                    childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom );
                    childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo );
                }

                // Horizontal props scaling
                if ( factor.from.x !== factor.to.x ) {
                    childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom );
                    childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo );
                }

                if ( restore ) {
                    $.effects.saveStyle( child );
                }

                // Animate children
                child.css( childFrom );
                child.animate( childTo, options.duration, options.easing, function() {

                    // Restore children
                    if ( restore ) {
                        $.effects.restoreStyle( child );
                    }
                } );
            } );
        }

        // Animate
        element.animate( to, {
            queue: false,
            duration: options.duration,
            easing: options.easing,
            complete: function() {

                var offset = element.offset();

                if ( to.opacity === 0 ) {
                    element.css( "opacity", from.opacity );
                }

                if ( !restore ) {
                    element
                        .css( "position", position === "static" ? "relative" : position )
                        .offset( offset );

                    // Need to save style here so that automatic style restoration
                    // doesn't restore to the original styles from before the animation.
                    $.effects.saveStyle( element );
                }

                done();
            }
        } );

    } );

} );

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

//>>label: Scale Effect
//>>group: Effects
//>>description: Grows or shrinks an element and its content.
//>>docs: http://api.jqueryui.com/scale-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/effects/effect-scale',[
            "jquery",
            "../version",
            "../effect",
            "./effect-size"
        ], factory );
    } else {

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

    return $.effects.define( "scale", function( options, done ) {

        // Create element
        var el = $( this ),
            mode = options.mode,
            percent = parseInt( options.percent, 10 ) ||
                ( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ),

            newOptions = $.extend( true, {
                from: $.effects.scaledDimensions( el ),
                to: $.effects.scaledDimensions( el, percent, options.direction || "both" ),
                origin: options.origin || [ "middle", "center" ]
            }, options );

        // Fade option to support puff
        if ( options.fade ) {
            newOptions.from.opacity = 1;
            newOptions.to.opacity = 0;
        }

        $.effects.effect.size.call( this, newOptions, done );
    } );

} );

/*
 * Metadata - jQuery plugin for parsing metadata from elements
 *
 * Copyright (c) 2006 John Resig, Yehuda Katz, Jï¿½Ã¶rn Zaefferer, Paul McLanahan
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.metadata.js 3640 2007-10-11 18:34:38Z pmclanahan $
 *
 */

/**
 * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
 * in the JSON will become a property of the element itself.
 *
 * There are four supported types of metadata storage:
 *
 *   attr:  Inside an attribute. The name parameter indicates *which* attribute.
 *
 *   class: Inside the class attribute, wrapped in curly braces: { }
 *
 *   elem:  Inside a child element (e.g. a script tag). The
 *          name parameter indicates *which* element.
 *   html5: Values are stored in data-* attributes.
 *
 * The metadata for an element is loaded the first time the element is accessed via jQuery.
 *
 * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
 * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
 *
 * @name $.metadata.setType
 *
 * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("class")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from the class attribute
 *
 * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("attr", "data")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a "data" attribute
 *
 * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
 * @before $.metadata.setType("elem", "script")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a nested script element
 *
 * @example <p id="one" class="some_class" data-item_id="1" data-item_label="Label">This is a p</p>
 * @before $.metadata.setType("html5")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a series of data-* attributes
 *
 * @param String type The encoding type
 * @param String name The name of the attribute to be used to get metadata (optional)
 * @cat Plugins/Metadata
 * @descr Sets the type of encoding to be used when loading metadata for the first time
 * @type undefined
 * @see metadata()
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define('jquery/jquery.metadata',["jquery"], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {


    $.extend({
        metadata : {
            defaults : {
                type: 'class',
                name: 'metadata',
                cre: /({.*})/,
                single: 'metadata',
                meta:'validate'
            },
            setType: function( type, name ){
                this.defaults.type = type;
                this.defaults.name = name;
            },
            get: function( elem, opts ){
                var settings = $.extend({},this.defaults,opts);
                // check for empty string in single property
                if (!settings.single.length) {
                    settings.single = 'metadata';
                }
                if (!settings.meta.length) {
                    settings.meta = 'validate';
                }

                var data = $.data(elem, settings.single);
                // returned cached data if it already exists
                if ( data ) return data;

                data = "{}";

                var getData = function(data) {
                    if(typeof data != "string") return data;

                    if( data.indexOf('{') < 0 ) {
                        data = eval("(" + data + ")");
                    }
                }

                var getObject = function(data) {
                    if(typeof data != "string") return data;

                    data = eval("(" + data + ")");
                    return data;
                }

                if ( settings.type == "html5" ) {
                    var object = {};
                    $( elem.attributes ).each(function() {
                        var name = this.nodeName;
                        if (name.indexOf('data-' + settings.meta) === 0) {
                            name = name.replace(/^data-/, '');
                        }
                        else {
                            return true;
                        }
                        object[name] = getObject(this.value);
                    });
                } else {
                    if ( settings.type == "class" ) {
                        var m = settings.cre.exec( elem.className );
                        if ( m )
                            data = m[1];
                    } else if ( settings.type == "elem" ) {
                        if( !elem.getElementsByTagName ) return;
                        var e = elem.getElementsByTagName(settings.name);
                        if ( e.length )
                            data = $.trim(e[0].innerHTML);
                    } else if ( elem.getAttribute != undefined ) {
                        var attr = elem.getAttribute( settings.name );
                        if ( attr )
                            data = attr;
                    }
                    object = getObject(data.indexOf("{") < 0 ? "{" + data + "}" : data);
                }

                $.data( elem, settings.single, object );
                return object;
            }
        }
    });

    /**
     * Returns the metadata object for the first member of the jQuery object.
     *
     * @name metadata
     * @descr Returns element's metadata object
     * @param Object opts An object contianing settings to override the defaults
     * @type jQuery
     * @cat Plugins/Metadata
     */
    $.fn.metadata = function( opts ){
        return $.metadata.get( this[0], opts );
    };

}));
/*!
 * jQuery Validation Plugin v1.19.5
 *
 * https://jqueryvalidation.org/
 *
 * Copyright (c) 2022 Jörn Zaefferer
 * Released under the MIT license
 */
(function( factory ) {
    if ( typeof define === "function" && define.amd ) {
        define( 'jquery/validate',["jquery", "jquery/jquery.metadata"], factory );
    } else if (typeof module === "object" && module.exports) {
        module.exports = factory( require( "jquery" ) );
    } else {
        factory( jQuery );
    }
}(function( $ ) {

    $.extend( $.fn, {

        // https://jqueryvalidation.org/validate/
        validate: function( options ) {

            // If nothing is selected, return nothing; can't chain anyway
            if ( !this.length ) {
                if ( options && options.debug && window.console ) {
                    console.warn( "Nothing selected, can't validate, returning nothing." );
                }
                return;
            }

            // Check if a validator for this form was already created
            var validator = $.data( this[ 0 ], "validator" );
            if ( validator ) {
                return validator;
            }

            // Add novalidate tag if HTML5.
            this.attr( "novalidate", "novalidate" );

            validator = new $.validator( options, this[ 0 ] );
            $.data( this[ 0 ], "validator", validator );

            if ( validator.settings.onsubmit ) {

                this.on( "click.validate", ":submit", function( event ) {

                    // Track the used submit button to properly handle scripted
                    // submits later.
                    validator.submitButton = event.currentTarget;

                    // Allow suppressing validation by adding a cancel class to the submit button
                    if ( $( this ).hasClass( "cancel" ) ) {
                        validator.cancelSubmit = true;
                    }

                    // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
                    if ( $( this ).attr( "formnovalidate" ) !== undefined ) {
                        validator.cancelSubmit = true;
                    }
                } );

                // Validate the form on submit
                this.on( "submit.validate", function( event ) {
                    if ( validator.settings.debug ) {

                        // Prevent form submit to be able to see console output
                        event.preventDefault();
                    }

                    function handle() {
                        var hidden, result;

                        // Insert a hidden input as a replacement for the missing submit button
                        // The hidden input is inserted in two cases:
                        //   - A user defined a `submitHandler`
                        //   - There was a pending request due to `remote` method and `stopRequest()`
                        //     was called to submit the form in case it's valid
                        if ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) {
                            hidden = $( "<input type='hidden'/>" )
                                .attr( "name", validator.submitButton.name )
                                .val( $( validator.submitButton ).val() )
                                .appendTo( validator.currentForm );
                        }

                        if ( validator.settings.submitHandler && !validator.settings.debug ) {
                            result = validator.settings.submitHandler.call( validator, validator.currentForm, event );
                            if ( hidden ) {

                                // And clean up afterwards; thanks to no-block-scope, hidden can be referenced
                                hidden.remove();
                            }
                            if ( result !== undefined ) {
                                return result;
                            }
                            return false;
                        }
                        return true;
                    }

                    // Prevent submit for invalid forms or custom submit handlers
                    if ( validator.cancelSubmit ) {
                        validator.cancelSubmit = false;
                        return handle();
                    }
                    if ( validator.form() ) {
                        if ( validator.pendingRequest ) {
                            validator.formSubmitted = true;
                            return false;
                        }
                        return handle();
                    } else {
                        validator.focusInvalid();
                        return false;
                    }
                } );
            }

            return validator;
        },

        // https://jqueryvalidation.org/valid/
        valid: function() {
            var valid, validator, errorList;

            if ( $( this[ 0 ] ).is( "form" ) ) {
                valid = this.validate().form();
            } else {
                errorList = [];
                valid = true;
                validator = $( this[ 0 ].form ).validate();
                this.each( function() {
                    valid = validator.element( this ) && valid;
                    if ( !valid ) {
                        errorList = errorList.concat( validator.errorList );
                    }
                } );
                validator.errorList = errorList;
            }
            return valid;
        },

        // https://jqueryvalidation.org/rules/
        rules: function( command, argument ) {
            var element = this[ 0 ],
                isContentEditable = typeof this.attr( "contenteditable" ) !== "undefined" && this.attr( "contenteditable" ) !== "false",
                settings, staticRules, existingRules, data, param, filtered;

            // If nothing is selected, return empty object; can't chain anyway
            if ( element == null ) {
                return;
            }

            if ( !element.form && isContentEditable ) {
                element.form = this.closest( "form" )[ 0 ];
                element.name = this.attr( "name" );
            }

            if ( element.form == null ) {
                return;
            }

            if ( command ) {
                settings = $.data( element.form, "validator" ).settings;
                staticRules = settings.rules;
                existingRules = $.validator.staticRules( element );
                switch ( command ) {
                    case "add":
                        $.extend( existingRules, $.validator.normalizeRule( argument ) );

                        // Remove messages from rules, but allow them to be set separately
                        delete existingRules.messages;
                        staticRules[ element.name ] = existingRules;
                        if ( argument.messages ) {
                            settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );
                        }
                        break;
                    case "remove":
                        if ( !argument ) {
                            delete staticRules[ element.name ];
                            return existingRules;
                        }
                        filtered = {};
                        $.each( argument.split( /\s/ ), function( index, method ) {
                            filtered[ method ] = existingRules[ method ];
                            delete existingRules[ method ];
                        } );
                        return filtered;
                }
            }

            data = $.validator.normalizeRules(
                $.extend(
                    {},
                    $.validator.metadataRules(element),
                    $.validator.classRules( element ),
                    $.validator.attributeRules( element ),
                    $.validator.dataRules( element ),
                    $.validator.staticRules( element )
                ), element );

            // Make sure required is at front
            if ( data.required ) {
                param = data.required;
                delete data.required;
                data = $.extend( { required: param }, data );
            }

            // Make sure remote is at back
            if ( data.remote ) {
                param = data.remote;
                delete data.remote;
                data = $.extend( data, { remote: param } );
            }

            return data;
        }
    } );

// JQuery trim is deprecated, provide a trim method based on String.prototype.trim
    var trim = function( str ) {

        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill
        return str.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "" );
    };

// Custom selectors
    $.extend( $.expr.pseudos || $.expr[ ":" ], {		// '|| $.expr[ ":" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support

        // https://jqueryvalidation.org/blank-selector/
        blank: function( a ) {
            return !trim( "" + $( a ).val() );
        },

        // https://jqueryvalidation.org/filled-selector/
        filled: function( a ) {
            var val = $( a ).val();
            return val !== null && !!trim( "" + val );
        },

        // https://jqueryvalidation.org/unchecked-selector/
        unchecked: function( a ) {
            return !$( a ).prop( "checked" );
        }
    } );

// Constructor for validator
    $.validator = function( options, form ) {
        this.settings = $.extend( true, {}, $.validator.defaults, options );
        this.currentForm = form;
        this.init();
    };

// https://jqueryvalidation.org/jQuery.validator.format/
    $.validator.format = function( source, params ) {
        if ( arguments.length === 1 ) {
            return function() {
                var args = $.makeArray( arguments );
                args.unshift( source );
                return $.validator.format.apply( this, args );
            };
        }
        if ( params === undefined ) {
            return source;
        }
        if ( arguments.length > 2 && params.constructor !== Array  ) {
            params = $.makeArray( arguments ).slice( 1 );
        }
        if ( params.constructor !== Array ) {
            params = [ params ];
        }
        $.each( params, function( i, n ) {
            source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() {
                return n;
            } );
        } );
        return source;
    };

    $.extend( $.validator, {

        defaults: {
            messages: {},
            groups: {},
            rules: {},
            errorClass: "error",
            pendingClass: "pending",
            validClass: "valid",
            errorElement: "label",
            focusCleanup: false,
            focusInvalid: true,
            errorContainer: $( [] ),
            errorLabelContainer: $( [] ),
            onsubmit: true,
            ignore: ":hidden",
            ignoreTitle: false,
            onfocusin: function( element ) {
                this.lastActive = element;

                // Hide error label and remove error class on focus if enabled
                if ( this.settings.focusCleanup ) {
                    if ( this.settings.unhighlight ) {
                        this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
                    }
                    this.hideThese( this.errorsFor( element ) );
                }
            },
            onfocusout: function( element ) {
                if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
                    this.element( element );
                }
            },
            onkeyup: function( element, event ) {

                // Avoid revalidate the field when pressing one of the following keys
                // Shift       => 16
                // Ctrl        => 17
                // Alt         => 18
                // Caps lock   => 20
                // End         => 35
                // Home        => 36
                // Left arrow  => 37
                // Up arrow    => 38
                // Right arrow => 39
                // Down arrow  => 40
                // Insert      => 45
                // Num lock    => 144
                // AltGr key   => 225
                var excludedKeys = [
                    16, 17, 18, 20, 35, 36, 37,
                    38, 39, 40, 45, 144, 225
                ];

                if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {
                    return;
                } else if ( element.name in this.submitted || element.name in this.invalid ) {
                    this.element( element );
                }
            },
            onclick: function( element ) {

                // Click on selects, radiobuttons and checkboxes
                if ( element.name in this.submitted ) {
                    this.element( element );

                    // Or option elements, check parent select in that case
                } else if ( element.parentNode.name in this.submitted ) {
                    this.element( element.parentNode );
                }
            },
            highlight: function( element, errorClass, validClass ) {
                if ( element.type === "radio" ) {
                    this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
                } else {
                    $( element ).addClass( errorClass ).removeClass( validClass );
                }
            },
            unhighlight: function( element, errorClass, validClass ) {
                if ( element.type === "radio" ) {
                    this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
                } else {
                    $( element ).removeClass( errorClass ).addClass( validClass );
                }
            }
        },

        // https://jqueryvalidation.org/jQuery.validator.setDefaults/
        setDefaults: function( settings ) {
            $.extend( $.validator.defaults, settings );
        },

        messages: {
            required: "This field is required.",
            remote: "Please fix this field.",
            email: "Please enter a valid email address.",
            url: "Please enter a valid URL.",
            date: "Please enter a valid date.",
            dateISO: "Please enter a valid date (ISO).",
            number: "Please enter a valid number.",
            digits: "Please enter only digits.",
            equalTo: "Please enter the same value again.",
            maxlength: $.validator.format( "Please enter no more than {0} characters." ),
            minlength: $.validator.format( "Please enter at least {0} characters." ),
            rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ),
            range: $.validator.format( "Please enter a value between {0} and {1}." ),
            max: $.validator.format( "Please enter a value less than or equal to {0}." ),
            min: $.validator.format( "Please enter a value greater than or equal to {0}." ),
            step: $.validator.format( "Please enter a multiple of {0}." )
        },

        autoCreateRanges: false,

        prototype: {

            init: function() {
                this.labelContainer = $( this.settings.errorLabelContainer );
                this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );
                this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );
                this.submitted = {};
                this.valueCache = {};
                this.pendingRequest = 0;
                this.pending = {};
                this.invalid = {};
                this.reset();

                var currentForm = this.currentForm,
                    groups = ( this.groups = {} ),
                    rules;
                $.each( this.settings.groups, function( key, value ) {
                    if ( typeof value === "string" ) {
                        value = value.split( /\s/ );
                    }
                    $.each( value, function( index, name ) {
                        groups[ name ] = key;
                    } );
                } );
                rules = this.settings.rules;
                $.each( rules, function( key, value ) {
                    rules[ key ] = $.validator.normalizeRule( value );
                } );

                function delegate( event ) {
                    var isContentEditable = typeof $( this ).attr( "contenteditable" ) !== "undefined" && $( this ).attr( "contenteditable" ) !== "false";

                    // Set form expando on contenteditable
                    if ( !this.form && isContentEditable ) {
                        this.form = $( this ).closest( "form" )[ 0 ];
                        this.name = $( this ).attr( "name" );
                    }

                    // Ignore the element if it belongs to another form. This will happen mainly
                    // when setting the `form` attribute of an input to the id of another form.
                    if ( currentForm !== this.form ) {
                        return;
                    }

                    var validator = $.data( this.form, "validator" ),
                        eventType = "on" + event.type.replace( /^validate/, "" ),
                        settings = validator.settings;
                    if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {
                        settings[ eventType ].call( validator, this, event );
                    }
                }

                $( this.currentForm )
                    .on( "focusin.validate focusout.validate keyup.validate",
                        ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
                        "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
                        "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
                        "[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate )

                    // Support: Chrome, oldIE
                    // "select" is provided as event.target when clicking a option
                    .on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );

                if ( this.settings.invalidHandler ) {
                    $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler );
                }
            },

            // https://jqueryvalidation.org/Validator.form/
            form: function() {
                this.checkForm();
                $.extend( this.submitted, this.errorMap );
                this.invalid = $.extend( {}, this.errorMap );
                if ( !this.valid() ) {
                    $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
                }
                this.showErrors();
                return this.valid();
            },

            checkForm: function() {
                this.prepareForm();
                for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {
                    this.check( elements[ i ] );
                }
                return this.valid();
            },

            // https://jqueryvalidation.org/Validator.element/
            element: function( element ) {
                var cleanElement = this.clean( element ),
                    checkElement = this.validationTargetFor( cleanElement ),
                    v = this,
                    result = true,
                    rs, group;

                if ( checkElement === undefined ) {
                    delete this.invalid[ cleanElement.name ];
                } else {
                    this.prepareElement( checkElement );
                    this.currentElements = $( checkElement );

                    // If this element is grouped, then validate all group elements already
                    // containing a value
                    group = this.groups[ checkElement.name ];
                    if ( group ) {
                        $.each( this.groups, function( name, testgroup ) {
                            if ( testgroup === group && name !== checkElement.name ) {
                                cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );
                                if ( cleanElement && cleanElement.name in v.invalid ) {
                                    v.currentElements.push( cleanElement );
                                    result = v.check( cleanElement ) && result;
                                }
                            }
                        } );
                    }

                    rs = this.check( checkElement ) !== false;
                    result = result && rs;
                    if ( rs ) {
                        this.invalid[ checkElement.name ] = false;
                    } else {
                        this.invalid[ checkElement.name ] = true;
                    }

                    if ( !this.numberOfInvalids() ) {

                        // Hide error containers on last error
                        this.toHide = this.toHide.add( this.containers );
                    }
                    this.showErrors();

                    // Add aria-invalid status for screen readers
                    $( element ).attr( "aria-invalid", !rs );
                }

                return result;
            },

            // https://jqueryvalidation.org/Validator.showErrors/
            showErrors: function( errors ) {
                if ( errors ) {
                    var validator = this;

                    // Add items to error list and map
                    $.extend( this.errorMap, errors );
                    this.errorList = $.map( this.errorMap, function( message, name ) {
                        return {
                            message: message,
                            element: validator.findByName( name )[ 0 ]
                        };
                    } );

                    // Remove items from success list
                    this.successList = $.grep( this.successList, function( element ) {
                        return !( element.name in errors );
                    } );
                }
                if ( this.settings.showErrors ) {
                    this.settings.showErrors.call( this, this.errorMap, this.errorList );
                } else {
                    this.defaultShowErrors();
                }
            },

            // https://jqueryvalidation.org/Validator.resetForm/
            resetForm: function() {
                if ( $.fn.resetForm ) {
                    $( this.currentForm ).resetForm();
                }
                this.invalid = {};
                this.submitted = {};
                this.prepareForm();
                this.hideErrors();
                var elements = this.elements()
                    .removeData( "previousValue" )
                    .removeAttr( "aria-invalid" );

                this.resetElements( elements );
            },

            resetElements: function( elements ) {
                var i;

                if ( this.settings.unhighlight ) {
                    for ( i = 0; elements[ i ]; i++ ) {
                        this.settings.unhighlight.call( this, elements[ i ],
                            this.settings.errorClass, "" );
                        this.findByName( elements[ i ].name ).removeClass( this.settings.validClass );
                    }
                } else {
                    elements
                        .removeClass( this.settings.errorClass )
                        .removeClass( this.settings.validClass );
                }
            },

            numberOfInvalids: function() {
                return this.objectLength( this.invalid );
            },

            objectLength: function( obj ) {
                /* jshint unused: false */
                var count = 0,
                    i;
                for ( i in obj ) {

                    // This check allows counting elements with empty error
                    // message as invalid elements
                    if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {
                        count++;
                    }
                }
                return count;
            },

            hideErrors: function() {
                this.hideThese( this.toHide );
            },

            hideThese: function( errors ) {
                errors.not( this.containers ).text( "" );
                this.addWrapper( errors ).hide();
            },

            valid: function() {
                return this.size() === 0;
            },

            size: function() {
                return this.errorList.length;
            },

            focusInvalid: function() {
                if ( this.settings.focusInvalid ) {
                    try {
                        $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )
                            .filter( ":visible" )
                            .trigger( "focus" )

                            // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
                            .trigger( "focusin" );
                    } catch ( e ) {

                        // Ignore IE throwing errors when focusing hidden elements
                    }
                }
            },

            findLastActive: function() {
                var lastActive = this.lastActive;
                return lastActive && $.grep( this.errorList, function( n ) {
                    return n.element.name === lastActive.name;
                } ).length === 1 && lastActive;
            },

            elements: function() {
                var validator = this,
                    rulesCache = {};

                // Select all valid inputs inside the form (no submit or reset buttons)
                return $( this.currentForm )
                    .find( "input, select, textarea, [contenteditable]" )
                    .not( ":submit, :reset, :image, :disabled" )
                    .not( this.settings.ignore )
                    .filter( function() {
                        var name = this.name || $( this ).attr( "name" ); // For contenteditable
                        var isContentEditable = typeof $( this ).attr( "contenteditable" ) !== "undefined" && $( this ).attr( "contenteditable" ) !== "false";

                        if ( !name && validator.settings.debug && window.console ) {
                            console.error( "%o has no name assigned", this );
                        }

                        // Set form expando on contenteditable
                        if ( isContentEditable ) {
                            this.form = $( this ).closest( "form" )[ 0 ];
                            this.name = name;
                        }

                        // Ignore elements that belong to other/nested forms
                        if ( this.form !== validator.currentForm ) {
                            return false;
                        }

                        // Select only the first element for each name, and only those with rules specified
                        if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
                            return false;
                        }

                        rulesCache[ name ] = true;
                        return true;
                    } );
            },

            clean: function( selector ) {
                return $( selector )[ 0 ];
            },

            errors: function() {
                var errorClass = this.settings.errorClass.split( " " ).join( "." );
                return $( this.settings.errorElement + "." + errorClass, this.errorContext );
            },

            resetInternals: function() {
                this.successList = [];
                this.errorList = [];
                this.errorMap = {};
                this.toShow = $( [] );
                this.toHide = $( [] );
            },

            reset: function() {
                this.resetInternals();
                this.currentElements = $( [] );
            },

            prepareForm: function() {
                this.reset();
                this.toHide = this.errors().add( this.containers );
            },

            prepareElement: function( element ) {
                this.reset();
                this.toHide = this.errorsFor( element );
            },

            elementValue: function( element ) {
                var $element = $( element ),
                    type = element.type,
                    isContentEditable = typeof $element.attr( "contenteditable" ) !== "undefined" && $element.attr( "contenteditable" ) !== "false",
                    val, idx;

                if ( type === "radio" || type === "checkbox" ) {
                    return this.findByName( element.name ).filter( ":checked" ).val();
                } else if ( type === "number" && typeof element.validity !== "undefined" ) {
                    return element.validity.badInput ? "NaN" : $element.val();
                }

                if ( isContentEditable ) {
                    val = $element.text();
                } else {
                    val = $element.val();
                }

                if ( type === "file" ) {

                    // Modern browser (chrome & safari)
                    if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) {
                        return val.substr( 12 );
                    }

                    // Legacy browsers
                    // Unix-based path
                    idx = val.lastIndexOf( "/" );
                    if ( idx >= 0 ) {
                        return val.substr( idx + 1 );
                    }

                    // Windows-based path
                    idx = val.lastIndexOf( "\\" );
                    if ( idx >= 0 ) {
                        return val.substr( idx + 1 );
                    }

                    // Just the file name
                    return val;
                }

                if ( typeof val === "string" ) {
                    return val.replace( /\r/g, "" );
                }
                return val;
            },

            check: function( element ) {
                element = this.validationTargetFor( this.clean( element ) );

                var rules = $( element ).rules(),
                    rulesCount = $.map( rules, function( n, i ) {
                        return i;
                    } ).length,
                    dependencyMismatch = false,
                    val = this.elementValue( element ),
                    result, method, rule, normalizer;

                // Prioritize the local normalizer defined for this element over the global one
                // if the former exists, otherwise user the global one in case it exists.
                if ( typeof rules.normalizer === "function" ) {
                    normalizer = rules.normalizer;
                } else if (	typeof this.settings.normalizer === "function" ) {
                    normalizer = this.settings.normalizer;
                }

                // If normalizer is defined, then call it to the changed value instead
                // of using the real one.
                // Note that `this` in the normalizer is `element`.
                if ( normalizer ) {
                    val = normalizer.call( element, val );

                    // Delete the normalizer from rules to avoid treating it as a pre-defined method.
                    delete rules.normalizer;
                }

                for ( method in rules ) {
                    rule = { method: method, parameters: rules[ method ] };
                    try {
                        result = $.validator.methods[ method ].call( this, val, element, rule.parameters );

                        // If a method indicates that the field is optional and therefore valid,
                        // don't mark it as valid when there are no other rules
                        if ( result === "dependency-mismatch" && rulesCount === 1 ) {
                            dependencyMismatch = true;
                            continue;
                        }
                        dependencyMismatch = false;

                        if ( result === "pending" ) {
                            this.toHide = this.toHide.not( this.errorsFor( element ) );
                            return;
                        }

                        if ( !result ) {
                            this.formatAndAdd( element, rule );
                            return false;
                        }
                    } catch ( e ) {
                        if ( this.settings.debug && window.console ) {
                            console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
                        }
                        if ( e instanceof TypeError ) {
                            e.message += ".  Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.";
                        }

                        throw e;
                    }
                }
                if ( dependencyMismatch ) {
                    return;
                }
                if ( this.objectLength( rules ) ) {
                    this.successList.push( element );
                }
                return true;
            },

            // Return the custom message for the given element and validation method
            // specified in the element's HTML5 data attribute
            // return the generic message if present and no method specific message is present
            customDataMessage: function( element, method ) {
                return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() +
                    method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" );
            },

            // Return the custom message for the given element name and validation method
            customMessage: function( name, method ) {
                var m = this.settings.messages[ name ];
                return m && ( m.constructor === String ? m : m[ method ] );
            },

            // Return the first defined argument, allowing empty strings
            findDefined: function() {
                for ( var i = 0; i < arguments.length; i++ ) {
                    if ( arguments[ i ] !== undefined ) {
                        return arguments[ i ];
                    }
                }
                return undefined;
            },

            // The second parameter 'rule' used to be a string, and extended to an object literal
            // of the following form:
            // rule = {
            //     method: "method name",
            //     parameters: "the given method parameters"
            // }
            //
            // The old behavior still supported, kept to maintain backward compatibility with
            // old code, and will be removed in the next major release.
            defaultMessage: function( element, rule ) {
                if ( typeof rule === "string" ) {
                    rule = { method: rule };
                }

                var message = this.findDefined(
                        this.customMessage( element.name, rule.method ),
                        this.customDataMessage( element, rule.method ),

                        // 'title' is never undefined, so handle empty string as undefined
                        !this.settings.ignoreTitle && element.title || undefined,
                        $.validator.messages[ rule.method ],
                        "<strong>Warning: No message defined for " + element.name + "</strong>"
                    ),
                    theregex = /\$?\{(\d+)\}/g;
                if ( typeof message === "function" ) {
                    message = message.call( this, rule.parameters, element );
                } else if ( theregex.test( message ) ) {
                    message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters );
                }

                return message;
            },

            formatAndAdd: function( element, rule ) {
                var message = this.defaultMessage( element, rule );

                this.errorList.push( {
                    message: message,
                    element: element,
                    method: rule.method
                } );

                this.errorMap[ element.name ] = message;
                this.submitted[ element.name ] = message;
            },

            addWrapper: function( toToggle ) {
                if ( this.settings.wrapper ) {
                    toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
                }
                return toToggle;
            },

            defaultShowErrors: function() {
                var i, elements, error;
                for ( i = 0; this.errorList[ i ]; i++ ) {
                    error = this.errorList[ i ];
                    if ( this.settings.highlight ) {
                        this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
                    }
                    this.showLabel( error.element, error.message );
                }
                if ( this.errorList.length ) {
                    this.toShow = this.toShow.add( this.containers );
                }
                if ( this.settings.success ) {
                    for ( i = 0; this.successList[ i ]; i++ ) {
                        this.showLabel( this.successList[ i ] );
                    }
                }
                if ( this.settings.unhighlight ) {
                    for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {
                        this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );
                    }
                }
                this.toHide = this.toHide.not( this.toShow );
                this.hideErrors();
                this.addWrapper( this.toShow ).show();
            },

            validElements: function() {
                return this.currentElements.not( this.invalidElements() );
            },

            invalidElements: function() {
                return $( this.errorList ).map( function() {
                    return this.element;
                } );
            },

            showLabel: function( element, message ) {
                var place, group, errorID, v,
                    error = this.errorsFor( element ),
                    elementID = this.idOrName( element ),
                    describedBy = $( element ).attr( "aria-describedby" );

                if ( error.length ) {

                    // Refresh error/success class
                    error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );

                    // Replace message on existing label
                    error.html( message );
                } else {

                    // Create error element
                    error = $( "<" + this.settings.errorElement + ">" )
                        .attr( "id", elementID + "-error" )
                        .addClass( this.settings.errorClass )
                        .html( message || "" );

                    // Maintain reference to the element to be placed into the DOM
                    place = error;
                    if ( this.settings.wrapper ) {

                        // Make sure the element is visible, even in IE
                        // actually showing the wrapped element is handled elsewhere
                        place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent();
                    }
                    if ( this.labelContainer.length ) {
                        this.labelContainer.append( place );
                    } else if ( this.settings.errorPlacement ) {
                        this.settings.errorPlacement.call( this, place, $( element ) );
                    } else {
                        place.insertAfter( element );
                    }

                    // Link error back to the element
                    if ( error.is( "label" ) ) {

                        // If the error is a label, then associate using 'for'
                        error.attr( "for", elementID );

                        // If the element is not a child of an associated label, then it's necessary
                        // to explicitly apply aria-describedby
                    } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) {
                        errorID = error.attr( "id" );

                        // Respect existing non-error aria-describedby
                        if ( !describedBy ) {
                            describedBy = errorID;
                        } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) {

                            // Add to end of list if not already present
                            describedBy += " " + errorID;
                        }
                        $( element ).attr( "aria-describedby", describedBy );

                        // If this element is grouped, then assign to all elements in the same group
                        group = this.groups[ element.name ];
                        if ( group ) {
                            v = this;
                            $.each( v.groups, function( name, testgroup ) {
                                if ( testgroup === group ) {
                                    $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm )
                                        .attr( "aria-describedby", error.attr( "id" ) );
                                }
                            } );
                        }
                    }
                }
                if ( !message && this.settings.success ) {
                    error.text( "" );
                    if ( typeof this.settings.success === "string" ) {
                        error.addClass( this.settings.success );
                    } else {
                        this.settings.success( error, element );
                    }
                }
                this.toShow = this.toShow.add( error );
            },

            errorsFor: function( element ) {
                var name = this.escapeCssMeta( this.idOrName( element ) ),
                    describer = $( element ).attr( "aria-describedby" ),
                    selector = "label[for='" + name + "'], label[for='" + name + "'] *";

                // 'aria-describedby' should directly reference the error element
                if ( describer ) {
                    selector = selector + ", #" + this.escapeCssMeta( describer )
                        .replace( /\s+/g, ", #" ) + ":visible";
                }

                return this
                    .errors()
                    .filter( selector );
            },

            // See https://api.jquery.com/category/selectors/, for CSS
            // meta-characters that should be escaped in order to be used with JQuery
            // as a literal part of a name/id or any selector.
            escapeCssMeta: function( string ) {
                if ( string === undefined ) {
                    return "";
                }

                return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" );
            },

            idOrName: function( element ) {
                return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );
            },

            validationTargetFor: function( element ) {

                // If radio/checkbox, validate first element in group instead
                if ( this.checkable( element ) ) {
                    element = this.findByName( element.name );
                }

                // Always apply ignore filter
                return $( element ).not( this.settings.ignore )[ 0 ];
            },

            checkable: function( element ) {
                return ( /radio|checkbox/i ).test( element.type );
            },

            findByName: function( name ) {
                return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" );
            },

            getLength: function( value, element ) {
                switch ( element.nodeName.toLowerCase() ) {
                    case "select":
                        return $( "option:selected", element ).length;
                    case "input":
                        if ( this.checkable( element ) ) {
                            return this.findByName( element.name ).filter( ":checked" ).length;
                        }
                }
                return value.length;
            },

            depend: function( param, element ) {
                return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;
            },

            dependTypes: {
                "boolean": function( param ) {
                    return param;
                },
                "string": function( param, element ) {
                    return !!$( param, element.form ).length;
                },
                "function": function( param, element ) {
                    return param( element );
                }
            },

            optional: function( element ) {
                var val = this.elementValue( element );
                return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch";
            },

            startRequest: function( element ) {
                if ( !this.pending[ element.name ] ) {
                    this.pendingRequest++;
                    $( element ).addClass( this.settings.pendingClass );
                    this.pending[ element.name ] = true;
                }
            },

            stopRequest: function( element, valid ) {
                this.pendingRequest--;

                // Sometimes synchronization fails, make sure pendingRequest is never < 0
                if ( this.pendingRequest < 0 ) {
                    this.pendingRequest = 0;
                }
                delete this.pending[ element.name ];
                $( element ).removeClass( this.settings.pendingClass );
                if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) {
                    $( this.currentForm ).trigger( "submit" );

                    // Remove the hidden input that was used as a replacement for the
                    // missing submit button. The hidden input is added by `handle()`
                    // to ensure that the value of the used submit button is passed on
                    // for scripted submits triggered by this method
                    if ( this.submitButton ) {
                        $( "input:hidden[name='" + this.submitButton.name + "']", this.currentForm ).remove();
                    }

                    this.formSubmitted = false;
                } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {
                    $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
                    this.formSubmitted = false;
                }
            },

            previousValue: function( element, method ) {
                method = typeof method === "string" && method || "remote";

                return $.data( element, "previousValue" ) || $.data( element, "previousValue", {
                    old: null,
                    valid: true,
                    message: this.defaultMessage( element, { method: method } )
                } );
            },

            // Cleans up all forms and elements, removes validator-specific events
            destroy: function() {
                this.resetForm();

                $( this.currentForm )
                    .off( ".validate" )
                    .removeData( "validator" )
                    .find( ".validate-equalTo-blur" )
                    .off( ".validate-equalTo" )
                    .removeClass( "validate-equalTo-blur" )
                    .find( ".validate-lessThan-blur" )
                    .off( ".validate-lessThan" )
                    .removeClass( "validate-lessThan-blur" )
                    .find( ".validate-lessThanEqual-blur" )
                    .off( ".validate-lessThanEqual" )
                    .removeClass( "validate-lessThanEqual-blur" )
                    .find( ".validate-greaterThanEqual-blur" )
                    .off( ".validate-greaterThanEqual" )
                    .removeClass( "validate-greaterThanEqual-blur" )
                    .find( ".validate-greaterThan-blur" )
                    .off( ".validate-greaterThan" )
                    .removeClass( "validate-greaterThan-blur" );
            }

        },

        classRuleSettings: {
            required: { required: true },
            email: { email: true },
            url: { url: true },
            date: { date: true },
            dateISO: { dateISO: true },
            number: { number: true },
            digits: { digits: true },
            creditcard: { creditcard: true }
        },

        addClassRules: function( className, rules ) {
            if ( className.constructor === String ) {
                this.classRuleSettings[ className ] = rules;
            } else {
                $.extend( this.classRuleSettings, className );
            }
        },

        classRules: function( element ) {
            var rules = {},
                classes = $( element ).attr( "class" );

            if ( classes ) {
                $.each( classes.split( " " ), function() {
                    if ( this in $.validator.classRuleSettings ) {
                        $.extend( rules, $.validator.classRuleSettings[ this ] );
                    }
                } );
            }
            return rules;
        },

        normalizeAttributeRule: function( rules, type, method, value ) {

            // Convert the value to a number for number inputs, and for text for backwards compability
            // allows type="date" and others to be compared as strings
            if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
                value = Number( value );

                // Support Opera Mini, which returns NaN for undefined minlength
                if ( isNaN( value ) ) {
                    value = undefined;
                }
            }

            if ( value || value === 0 ) {
                rules[ method ] = value;
            } else if ( type === method && type !== "range" ) {

                // Exception: the jquery validate 'range' method
                // does not test for the html5 'range' type
                rules[ type === "date" ? "dateISO" : method ] = true;
            }
        },

        attributeRules: function( element ) {
            var rules = {},
                $element = $( element ),
                type = element.getAttribute( "type" ),
                method, value;

            for ( method in $.validator.methods ) {

                // Support for <input required> in both html5 and older browsers
                if ( method === "required" ) {
                    value = element.getAttribute( method );

                    // Some browsers return an empty string for the required attribute
                    // and non-HTML5 browsers might have required="" markup
                    if ( value === "" ) {
                        value = true;
                    }

                    // Force non-HTML5 browsers to return bool
                    value = !!value;
                } else {
                    value = $element.attr( method );
                }

                this.normalizeAttributeRule( rules, type, method, value );
            }

            // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
            if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
                delete rules.maxlength;
            }

            return rules;
        },

        metadataRules: function (element) {
            if (!$.metadata) {
                return {};
            }

            var meta = $.data(element.form, 'validator').settings.meta;
            return meta ?
                $(element).metadata()[meta] :
                $(element).metadata();
        },

        dataRules: function( element ) {
            var rules = {},
                $element = $( element ),
                type = element.getAttribute( "type" ),
                method, value;

            for ( method in $.validator.methods ) {
                value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );

                // Cast empty attributes like `data-rule-required` to `true`
                if ( value === "" ) {
                    value = true;
                }

                this.normalizeAttributeRule( rules, type, method, value );
            }
            return rules;
        },

        staticRules: function( element ) {
            var rules = {},
                validator = $.data( element.form, "validator" );

            if ( validator.settings.rules ) {
                rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};
            }
            return rules;
        },

        normalizeRules: function( rules, element ) {

            // Handle dependency check
            $.each( rules, function( prop, val ) {

                // Ignore rule when param is explicitly false, eg. required:false
                if ( val === false ) {
                    delete rules[ prop ];
                    return;
                }
                if ( val.param || val.depends ) {
                    var keepRule = true;
                    switch ( typeof val.depends ) {
                        case "string":
                            keepRule = !!$( val.depends, element.form ).length;
                            break;
                        case "function":
                            keepRule = val.depends.call( element, element );
                            break;
                    }
                    if ( keepRule ) {
                        rules[ prop ] = val.param !== undefined ? val.param : true;
                    } else {
                        $.data( element.form, "validator" ).resetElements( $( element ) );
                        delete rules[ prop ];
                    }
                }
            } );

            // Evaluate parameters
            $.each( rules, function( rule, parameter ) {
                rules[ rule ] = typeof parameter === "function" && rule !== "normalizer" ? parameter( element ) : parameter;
            } );

            // Clean number parameters
            $.each( [ "minlength", "maxlength" ], function() {
                if ( rules[ this ] ) {
                    rules[ this ] = Number( rules[ this ] );
                }
            } );
            $.each( [ "rangelength", "range" ], function() {
                var parts;
                if ( rules[ this ] ) {
                    if ( Array.isArray( rules[ this ] ) ) {
                        rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];
                    } else if ( typeof rules[ this ] === "string" ) {
                        parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ );
                        rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];
                    }
                }
            } );

            if ( $.validator.autoCreateRanges ) {

                // Auto-create ranges
                if ( rules.min != null && rules.max != null ) {
                    rules.range = [ rules.min, rules.max ];
                    delete rules.min;
                    delete rules.max;
                }
                if ( rules.minlength != null && rules.maxlength != null ) {
                    rules.rangelength = [ rules.minlength, rules.maxlength ];
                    delete rules.minlength;
                    delete rules.maxlength;
                }
            }

            return rules;
        },

        // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
        normalizeRule: function( data ) {
            if ( typeof data === "string" ) {
                var transformed = {};
                $.each( data.split( /\s/ ), function() {
                    transformed[ this ] = true;
                } );
                data = transformed;
            }
            return data;
        },

        // https://jqueryvalidation.org/jQuery.validator.addMethod/
        addMethod: function( name, method, message ) {
            $.validator.methods[ name ] = method;
            $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];
            if ( method.length < 3 ) {
                $.validator.addClassRules( name, $.validator.normalizeRule( name ) );
            }
        },

        // https://jqueryvalidation.org/jQuery.validator.methods/
        methods: {

            // https://jqueryvalidation.org/required-method/
            required: function( value, element, param ) {

                // Check if dependency is met
                if ( !this.depend( param, element ) ) {
                    return "dependency-mismatch";
                }
                if ( element.nodeName.toLowerCase() === "select" ) {

                    // Could be an array for select-multiple or a string, both are fine this way
                    var val = $( element ).val();
                    return val && val.length > 0;
                }
                if ( this.checkable( element ) ) {
                    return this.getLength( value, element ) > 0;
                }
                return value !== undefined && value !== null && value.length > 0;
            },

            // https://jqueryvalidation.org/email-method/
            email: function( value, element ) {

                // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
                // Retrieved 2014-01-14
                // If you have a problem with this implementation, report a bug against the above spec
                // Or use custom methods to implement your own email validation
                return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );
            },

            // https://jqueryvalidation.org/url-method/
            url: function( value, element ) {

                // Copyright (c) 2010-2013 Diego Perini, MIT licensed
                // https://gist.github.com/dperini/729294
                // see also https://mathiasbynens.be/demo/url-regex
                // modified to allow protocol-relative URLs
                return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value );
            },

            // https://jqueryvalidation.org/date-method/
            date: ( function() {
                var called = false;

                return function( value, element ) {
                    if ( !called ) {
                        called = true;
                        if ( this.settings.debug && window.console ) {
                            console.warn(
                                "The `date` method is deprecated and will be removed in version '2.0.0'.\n" +
                                "Please don't use it, since it relies on the Date constructor, which\n" +
                                "behaves very differently across browsers and locales. Use `dateISO`\n" +
                                "instead or one of the locale specific methods in `localizations/`\n" +
                                "and `additional-methods.js`."
                            );
                        }
                    }

                    return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );
                };
            }() ),

            // https://jqueryvalidation.org/dateISO-method/
            dateISO: function( value, element ) {
                return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
            },

            // https://jqueryvalidation.org/number-method/
            number: function( value, element ) {
                return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
            },

            // https://jqueryvalidation.org/digits-method/
            digits: function( value, element ) {
                return this.optional( element ) || /^\d+$/.test( value );
            },

            // https://jqueryvalidation.org/minlength-method/
            minlength: function( value, element, param ) {
                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
                return this.optional( element ) || length >= param;
            },

            // https://jqueryvalidation.org/maxlength-method/
            maxlength: function( value, element, param ) {
                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
                return this.optional( element ) || length <= param;
            },

            // https://jqueryvalidation.org/rangelength-method/
            rangelength: function( value, element, param ) {
                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
                return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
            },

            // https://jqueryvalidation.org/min-method/
            min: function( value, element, param ) {
                return this.optional( element ) || value >= param;
            },

            // https://jqueryvalidation.org/max-method/
            max: function( value, element, param ) {
                return this.optional( element ) || value <= param;
            },

            // https://jqueryvalidation.org/range-method/
            range: function( value, element, param ) {
                return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
            },

            // https://jqueryvalidation.org/step-method/
            step: function( value, element, param ) {
                var type = $( element ).attr( "type" ),
                    errorMessage = "Step attribute on input type " + type + " is not supported.",
                    supportedTypes = [ "text", "number", "range" ],
                    re = new RegExp( "\\b" + type + "\\b" ),
                    notSupported = type && !re.test( supportedTypes.join() ),
                    decimalPlaces = function( num ) {
                        var match = ( "" + num ).match( /(?:\.(\d+))?$/ );
                        if ( !match ) {
                            return 0;
                        }

                        // Number of digits right of decimal point.
                        return match[ 1 ] ? match[ 1 ].length : 0;
                    },
                    toInt = function( num ) {
                        return Math.round( num * Math.pow( 10, decimals ) );
                    },
                    valid = true,
                    decimals;

                // Works only for text, number and range input types
                // TODO find a way to support input types date, datetime, datetime-local, month, time and week
                if ( notSupported ) {
                    throw new Error( errorMessage );
                }

                decimals = decimalPlaces( param );

                // Value can't have too many decimals
                if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {
                    valid = false;
                }

                return this.optional( element ) || valid;
            },

            // https://jqueryvalidation.org/equalTo-method/
            equalTo: function( value, element, param ) {

                // Bind to the blur event of the target in order to revalidate whenever the target field is updated
                var target = $( param );
                if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) {
                    target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() {
                        $( element ).valid();
                    } );
                }
                return value === target.val();
            },

            // https://jqueryvalidation.org/remote-method/
            remote: function( value, element, param, method ) {
                if ( this.optional( element ) ) {
                    return "dependency-mismatch";
                }

                method = typeof method === "string" && method || "remote";

                var previous = this.previousValue( element, method ),
                    validator, data, optionDataString;

                if ( !this.settings.messages[ element.name ] ) {
                    this.settings.messages[ element.name ] = {};
                }
                previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];
                this.settings.messages[ element.name ][ method ] = previous.message;

                param = typeof param === "string" && { url: param } || param;
                optionDataString = $.param( $.extend( { data: value }, param.data ) );
                if ( previous.old === optionDataString ) {
                    return previous.valid;
                }

                previous.old = optionDataString;
                validator = this;
                this.startRequest( element );
                data = {};
                data[ element.name ] = value;
                $.ajax( $.extend( true, {
                    mode: "abort",
                    port: "validate" + element.name,
                    dataType: "json",
                    data: data,
                    context: validator.currentForm,
                    success: function( response ) {
                        var valid = response === true || response === "true",
                            errors, message, submitted;

                        validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
                        if ( valid ) {
                            submitted = validator.formSubmitted;
                            validator.resetInternals();
                            validator.toHide = validator.errorsFor( element );
                            validator.formSubmitted = submitted;
                            validator.successList.push( element );
                            validator.invalid[ element.name ] = false;
                            validator.showErrors();
                        } else {
                            errors = {};
                            message = response || validator.defaultMessage( element, { method: method, parameters: value } );
                            errors[ element.name ] = previous.message = message;
                            validator.invalid[ element.name ] = true;
                            validator.showErrors( errors );
                        }
                        previous.valid = valid;
                        validator.stopRequest( element, valid );
                    }
                }, param ) );
                return "pending";
            }
        }

    } );

// Ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()

    var pendingRequests = {},
        ajax;

// Use a prefilter if available (1.5+)
    if ( $.ajaxPrefilter ) {
        $.ajaxPrefilter( function( settings, _, xhr ) {
            var port = settings.port;
            if ( settings.mode === "abort" ) {
                if ( pendingRequests[ port ] ) {
                    pendingRequests[ port ].abort();
                }
                pendingRequests[ port ] = xhr;
            }
        } );
    } else {

        // Proxy ajax
        ajax = $.ajax;
        $.ajax = function( settings ) {
            var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
                port = ( "port" in settings ? settings : $.ajaxSettings ).port;
            if ( mode === "abort" ) {
                if ( pendingRequests[ port ] ) {
                    pendingRequests[ port ].abort();
                }
                pendingRequests[ port ] = ajax.apply( this, arguments );
                return pendingRequests[ port ];
            }
            return ajax.apply( this, arguments );
        };
    }
    return $;
}));

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

define('mage/validation',[
    'jquery',
    'moment',
    'mageUtils',
    'jquery-ui-modules/widget',
    'jquery/validate',
    'mage/translate'
], function ($, moment, utils) {
    'use strict';

    var creditCartTypes, rules, showLabel, originValidateDelegate;

    $.extend(true, $, {
        // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions
        mage: {
            /**
             * Check if string is empty with trim
             * @param {String} value
             */
            isEmpty: function (value) {
                return value === '' || value === undefined ||
                    value == null || value.length === 0 || /^\s+$/.test(value);
            },

            /**
             * Check if string is empty no trim
             * @param {String} value
             */
            isEmptyNoTrim: function (value) {
                return value === '' || value == null || value.length === 0;
            },

            /**
             * Checks if {value} is between numbers {from} and {to}
             * @param {String} value
             * @param {String} from
             * @param {String} to
             * @returns {Boolean}
             */
            isBetween: function (value, from, to) {
                return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&
                    ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));
            },

            /**
             * Parse price string
             * @param {String} value
             */
            parseNumber: function (value) {
                var isDot, isComa;

                if (typeof value !== 'string') {
                    return parseFloat(value);
                }
                isDot = value.indexOf('.');
                isComa = value.indexOf(',');

                if (isDot !== -1 && isComa !== -1) {
                    if (isComa > isDot) {
                        value = value.replace('.', '').replace(',', '.');
                    } else {
                        value = value.replace(',', '');
                    }
                } else if (isComa !== -1) {
                    value = value.replace(',', '.');
                }

                return parseFloat(value);
            },

            /**
             * Removes HTML tags and space characters, numbers and punctuation.
             *
             * @param {String} value - Value being stripped.
             * @return {String}
             */
            stripHtml: function (value) {
                return value.replace(/<.[^<>]*?>/g, ' ').replace(/&nbsp;|&#160;/gi, ' ')
                    .replace(/[0-9.(),;:!?%#$'"_+=\/-]*/g, '');
            }
        }
    });

    /**
     * @param {String} name
     * @param {*} method
     * @param {*} message
     * @param {*} dontSkip
     */
    $.validator.addMethod = function (name, method, message, dontSkip) {
        $.validator.methods[name] = method;
        $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];

        if (method.length < 3 || dontSkip) {
            $.validator.addClassRules(name, $.validator.normalizeRule(name));
        }
    };

    /**
     * Javascript object with credit card types
     * 0 - regexp for card number
     * 1 - regexp for cvn
     * 2 - check or not credit card number trough Luhn algorithm by
     */
    creditCartTypes = {
        'SO': [
            new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'),
            new RegExp('^([0-9]{3}|[0-9]{4})?$'),
            true
        ],
        'SM': [
            new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|' +
                '(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|' +
                '(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|' +
                '(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|' +
                '(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|' +
                '(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'),
            true
        ],
        'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
        'MC': [
            new RegExp('^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$'),
            new RegExp('^[0-9]{3}$'),
            true
        ],
        'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
        'DI': [new RegExp('^(6011(0|[2-4]|74|7[7-9]|8[6-9]|9)|6(4[4-9]|5))\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'JCB': [new RegExp('^35(2[8-9]|[3-8])\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'DN': [new RegExp('^(3(0[0-5]|095|6|[8-9]))\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'UN': [
            new RegExp('^(622(1(2[6-9]|[3-9])|[3-8]|9([[0-1]|2[0-5]))|62[4-6]|628([2-8]))\\d*?$'),
            new RegExp('^[0-9]{3}$'),
            true
        ],
        'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\d*$'), new RegExp('^[0-9]{3}$'), true],
        'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\d*$'), new RegExp('^[0-9]{3}$'), true]
    };

    /**
     * validate credit card number using mod10
     * @param {String} s
     * @return {Boolean}
     */
    function validateCreditCard(s) {
        // remove non-numerics
        var v = '0123456789',
            w = '',
            i, j, k, m, c, a, x;

        for (i = 0; i < s.length; i++) {
            x = s.charAt(i);

            if (v.indexOf(x, 0) !== -1) {
                w += x;
            }
        }
        // validate number
        j = w.length / 2;
        k = Math.floor(j);
        m = Math.ceil(j) - k;
        c = 0;

        for (i = 0; i < k; i++) {
            a = w.charAt(i * 2 + m) * 2;
            c += a > 9 ? Math.floor(a / 10 + a % 10) : a;
        }

        for (i = 0; i < k + m; i++) {
            c += w.charAt(i * 2 + 1 - m) * 1;
        }

        return c % 10 === 0;
    }

    /**
     * validate all table required inputs at once, using single hidden input
     * @param {String} value
     * @param {HTMLElement} element
     *
     * @return {Boolean}
     */
    function tableSingleValidation(value, element) {
        var empty = $(element).closest('table')
            .find('input.required-option:visible')
            .filter(function (i, el) {
                if ($(el).is('disabled')) {
                    return $.mage.isEmpty(el.value);
                }
            })
            .length;

        return empty === 0;
    }

    /**
     *
     * @param {float} qty
     * @param {float} qtyIncrements
     * @returns {float}
     */
    function resolveModulo(qty, qtyIncrements) {
        var divideEpsilon = 10000,
            epsilon,
            remainder;

        while (qtyIncrements < 1) {
            qty *= 10;
            qtyIncrements *= 10;
        }

        epsilon = qtyIncrements / divideEpsilon;
        remainder = qty % qtyIncrements;

        if (Math.abs(remainder - qtyIncrements) < epsilon ||
            Math.abs(remainder) < epsilon) {
            remainder = 0;
        }

        return remainder;
    }

    /**
     * Collection of validation rules including rules from additional-methods.js
     * @type {Object}
     */
    rules = {
        'max-words': [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length <= params;
            },
            $.mage.__('Please enter {0} words or less.')
        ],
        'min-words': [
            function (value, element, params) {
                return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params;
            },
            $.mage.__('Please enter at least {0} words.')
        ],
        'range-words': [
            function (value, element, params) {
                return this.optional(element) ||
                    $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params[0] &&
                    value.match(/bw+b/g).length < params[1];
            },
            $.mage.__('Please enter between {0} and {1} words.')
        ],
        'letters-with-basic-punc': [
            function (value, element) {
                return this.optional(element) || /^[a-z\-.,()'\"\s]+$/i.test(value);
            },
            $.mage.__('Letters or punctuation only please')
        ],
        'alphanumeric': [
            function (value, element) {
                return this.optional(element) || /^\w+$/i.test(value);
            },
            $.mage.__('Letters, numbers, spaces or underscores only please')
        ],
        'letters-only': [
            function (value, element) {
                return this.optional(element) || /^[a-z]+$/i.test(value);
            },
            $.mage.__('Letters only please')
        ],
        'no-whitespace': [
            function (value, element) {
                return this.optional(element) || /^\S+$/i.test(value);
            },
            $.mage.__('No white space please')
        ],
        'no-marginal-whitespace': [
            function (value, element) {
                return this.optional(element) || !/^\s+|\s+$/i.test(value);
            },
            $.mage.__('No marginal white space please')
        ],
        'zip-range': [
            function (value, element) {
                return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value);
            },
            $.mage.__('Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx')
        ],
        'integer': [
            function (value, element) {
                return this.optional(element) || /^-?\d+$/.test(value);
            },
            $.mage.__('A positive or negative non-decimal number please')
        ],
        'vinUS': [
            function (v) {
                var i, n, d, f, cd, cdv, LL, VL, FL, rs;

                /* eslint-disable max-depth */
                if (v.length !== 17) {
                    return false;
                }

                LL = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L',
                    'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
                VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];
                FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
                rs = 0;

                for (i = 0; i < 17; i++) {
                    f = FL[i];
                    d = v.slice(i, i + 1);

                    if (i === 8) {
                        cdv = d;
                    }

                    if (!isNaN(d)) {
                        d *= f;
                    } else {
                        for (n = 0; n < LL.length; n++) {
                            if (d.toUpperCase() === LL[n]) {
                                d = VL[n];
                                d *= f;

                                if (isNaN(cdv) && n === 8) {
                                    cdv = LL[n];
                                }
                                break;
                            }
                        }
                    }
                    rs += d;
                }

                /* eslint-enable max-depth */
                cd = rs % 11;

                if (cd === 10) {
                    cd = 'X';
                }

                if (cd === cdv) {
                    return true;
                }

                return false;
            },
            $.mage.__('The specified vehicle identification number (VIN) is invalid.')
        ],
        'dateITA': [
            function (value, element) {
                var check = false,
                    re = /^\d{1,2}\/\d{1,2}\/\d{4}$/,
                    adata, gg, mm, aaaa, xdata;

                if (re.test(value)) {
                    adata = value.split('/');
                    gg = parseInt(adata[0], 10);
                    mm = parseInt(adata[1], 10);
                    aaaa = parseInt(adata[2], 10);
                    xdata = new Date(aaaa, mm - 1, gg);

                    if (xdata.getFullYear() === aaaa &&
                        xdata.getMonth() === mm - 1 &&
                        xdata.getDate() === gg
                    ) {
                        check = true;
                    } else {
                        check = false;
                    }
                } else {
                    check = false;
                }

                return this.optional(element) || check;
            },
            $.mage.__('Please enter a correct date')
        ],
        'dateNL': [
            function (value, element) {
                return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value);
            },
            'Vul hier een geldige datum in.'
        ],
        'time': [
            function (value, element) {
                return this.optional(element) || /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value);
            },
            $.mage.__('Please enter a valid time, between 00:00 and 23:59')
        ],
        'time12h': [
            function (value, element) {
                return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\s[AP]M))$/i.test(value);
            },
            $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm')
        ],
        'phoneUS': [
            function (phoneNumber, element) {
                phoneNumber = phoneNumber.replace(/\s+/g, '');

                return this.optional(element) || phoneNumber.length > 9 &&
                    phoneNumber.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
            },
            $.mage.__('Please specify a valid phone number')
        ],
        'phoneUK': [
            function (phoneNumber, element) {
                return this.optional(element) || phoneNumber.length > 9 &&
                    phoneNumber.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/);
            },
            $.mage.__('Please specify a valid phone number')
        ],
        'mobileUK': [
            function (phoneNumber, element) {
                return this.optional(element) || phoneNumber.length > 9 &&
                    phoneNumber.match(/^((0|\+44)7\d{3}\s?\d{6})$/);
            },
            $.mage.__('Please specify a valid mobile number')
        ],
        'stripped-min-length': [
            function (value, element, param) {
                return value.length >= param;
            },
            $.mage.__('Please enter at least {0} characters')
        ],

        /* detect chars that would require more than 3 bytes */
        'validate-no-utf8mb4-characters': [
            function (value) {
                var validator = this,
                    message = $.mage.__('Please remove invalid characters: {0}.'),
                    matches = value.match(/(?:[\uD800-\uDBFF][\uDC00-\uDFFF])/g),
                    result = matches === null;

                if (!result) {
                    validator.charErrorMessage = message.replace('{0}', matches.join());
                }

                return result;
            }, function () {
                return this.charErrorMessage;
            }
        ],

        /* eslint-disable max-len */
        'email2': [
            function (value, element) {
                return this.optional(element) ||
                    /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
            },
            $.validator.messages.email
        ],
        'url2': [
            function (value, element) {
                return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
            },
            $.validator.messages.url
        ],

        /* eslint-enable max-len */
        'credit-card-types': [
            function (value, element, param) {
                var validTypes;

                if (/[^0-9-]+/.test(value)) {
                    return false;
                }
                value = value.replace(/\D/g, '');

                validTypes = 0x0000;

                if (param.mastercard) {
                    validTypes |= 0x0001;
                }

                if (param.visa) {
                    validTypes |= 0x0002;
                }

                if (param.amex) {
                    validTypes |= 0x0004;
                }

                if (param.dinersclub) {
                    validTypes |= 0x0008;
                }

                if (param.enroute) {
                    validTypes |= 0x0010;
                }

                if (param.discover) {
                    validTypes |= 0x0020;
                }

                if (param.jcb) {
                    validTypes |= 0x0040;
                }

                if (param.unknown) {
                    validTypes |= 0x0080;
                }

                if (param.all) {
                    validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
                }

                if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard
                    return value.length === 16;
                }

                if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
                    return value.length === 16;
                }

                if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex
                    return value.length === 15;
                }

                if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub
                    return value.length === 14;
                }

                if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute
                    return value.length === 15;
                }

                if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
                    return value.length === 16;
                }

                if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
                    return value.length === 16;
                }

                if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
                    return value.length === 15;
                }

                if (validTypes & 0x0080) { //unknown
                    return true;
                }

                return false;
            },
            $.mage.__('Please enter a valid credit card number.')
        ],

        /* eslint-disable max-len */
        'ipv4': [
            function (value, element) {
                return this.optional(element) ||
                    /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);
            },
            $.mage.__('Please enter a valid IP v4 address.')
        ],
        'ipv6': [
            function (value, element) {
                return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
            },
            $.mage.__('Please enter a valid IP v6 address.')
        ],

        /* eslint-enable max-len */
        'pattern': [
            function (value, element, param) {
                return this.optional(element) || new RegExp(param).test(value);
            },
            $.mage.__('Invalid format.')
        ],
        'allow-container-className': [
            function (element) {
                if (element.type === 'radio' || element.type === 'checkbox') {
                    return $(element).hasClass('change-container-classname');
                }
            },
            ''
        ],
        'validate-no-html-tags': [
            function (value) {
                return !/<(\/)?\w+/.test(value);
            },
            $.mage.__('HTML tags are not allowed.')
        ],
        'validate-select': [
            function (value) {
                return value !== 'none' && value != null && value.length !== 0;
            },
            $.mage.__('Please select an option.')
        ],
        'validate-no-empty': [
            function (value) {
                return !$.mage.isEmpty(value);
            },
            $.mage.__('Empty Value.')
        ],
        'validate-alphanum-with-spaces': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);
            },
            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.')
        ],
        'validate-data': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
            },
            $.mage.__('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.') //eslint-disable-line max-len
        ],
        'validate-street': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v);
            },
            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), spaces and "#" in this field.')
        ],
        'validate-phoneStrict': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')
        ],
        'validate-phoneLax': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) ||
                    /^((\d[\-. ]?)?((\(\d{3}\))|\d{3}))?[\-. ]?\d{3}[\-. ]?\d{4}$/.test(v);
            },
            $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')
        ],
        'validate-fax': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
            },
            $.mage.__('Please enter a valid fax number (Ex: 123-456-7890).')
        ],
        'validate-email': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i.test(v); //eslint-disable-line max-len
            },
            $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')
        ],
        'validate-emailSender': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[\S ]+$/.test(v);
            },
            $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')
        ],
        'validate-password': [
            function (v) {
                var pass;

                if (v == null) {
                    return false;
                }
                //strip leading and trailing spaces
                pass = v.trim();

                if (!pass.length) {
                    return true;
                }

                return !(pass.length > 0 && pass.length < 6);
            },
            $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')
        ],
        'validate-admin-password': [
            function (v) {
                var pass;

                if (v == null) {
                    return false;
                }
                pass = v.trim();
                // strip leading and trailing spaces
                if (pass.length === 0) {
                    return true;
                }

                if (!/[a-z]/i.test(v) || !/[0-9]/.test(v)) {
                    return false;
                }

                if (pass.length < 7) {
                    return false;
                }

                return true;
            },
            $.mage.__('Please enter 7 or more characters, using both numeric and alphabetic.')
        ],
        'validate-customer-password': [
            function (v, elm) {
                var validator = this,
                    counter = 0,
                    passwordMinLength = $(elm).data('password-min-length'),
                    passwordMinCharacterSets = $(elm).data('password-min-character-sets'),
                    pass = v.trim(),
                    result = pass.length >= passwordMinLength;

                if (result === false) {
                    validator.passwordErrorMessage = $.mage.__('Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.').replace('%1', passwordMinLength); //eslint-disable-line max-len

                    return result;
                }

                if (pass.match(/\d+/)) {
                    counter++;
                }

                if (pass.match(/[a-z]+/)) {
                    counter++;
                }

                if (pass.match(/[A-Z]+/)) {
                    counter++;
                }

                if (pass.match(/[^a-zA-Z0-9]+/)) {
                    counter++;
                }

                if (counter < passwordMinCharacterSets) {
                    result = false;
                    validator.passwordErrorMessage = $.mage.__('Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.').replace('%1', passwordMinCharacterSets); //eslint-disable-line max-len
                }

                return result;
            }, function () {
                return this.passwordErrorMessage;
            }
        ],
        'validate-url': [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');

                return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v); //eslint-disable-line max-len

            },
            $.mage.__('Please enter a valid URL. Protocol is required (http://, https:// or ftp://).')
        ],
        'validate-clean-url': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v); //eslint-disable-line max-len

            },
            $.mage.__('Please enter a valid URL. For example http://www.example.com or www.example.com.')
        ],
        'validate-xml-identifier': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v);

            },
            $.mage.__('Please enter a valid XML-identifier (Ex: something_1, block5, id-4).')
        ],
        'validate-ssn': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);

            },
            $.mage.__('Please enter a valid social security number (Ex: 123-45-6789).')
        ],
        'validate-zip-us': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);

            },
            $.mage.__('Please enter a valid zip code (Ex: 90602 or 90602-1234).')
        ],
        'validate-date-au': [
            function (v) {
                var regex, d;

                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;

                if ($.mage.isEmpty(v) || !regex.test(v)) {
                    return false;
                }
                d = new Date(v.replace(regex, '$2/$1/$3'));

                return parseInt(RegExp.$2, 10) === 1 + d.getMonth() &&
                    parseInt(RegExp.$1, 10) === d.getDate() &&
                    parseInt(RegExp.$3, 10) === d.getFullYear();

            },
            $.mage.__('Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.')
        ],
        'validate-currency-dollar': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v); //eslint-disable-line max-len

            },
            $.mage.__('Please enter a valid $ amount. For example $100.00.')
        ],
        'validate-not-negative-number': [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);

                return !isNaN(v) && v >= 0;

            },
            $.mage.__('Please enter a number 0 or greater in this field.')
        ],
        // validate-not-negative-number should be replaced in all places with this one and then removed
        'validate-zero-or-greater': [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);

                return !isNaN(v) && v >= 0;

            },
            $.mage.__('Please enter a number 0 or greater in this field.')
        ],
        'validate-greater-than-zero': [
            function (v) {
                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }
                v = $.mage.parseNumber(v);

                return !isNaN(v) && v > 0;
            },
            $.mage.__('Please enter a number greater than 0 in this field.')
        ],
        'validate-css-length': [
            function (v) {
                if (v !== '') {
                    return (/^[0-9]*\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);
                }

                return true;
            },
            $.mage.__('Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).')
        ],
        // Additional methods
        'validate-number': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || !isNaN($.mage.parseNumber(v)) && /^\s*-?\d*(\.\d*)?\s*$/.test(v);
            },
            $.mage.__('Please enter a valid number in this field.')
        ],
        'required-number': [
            function (v) {
                return !!v.length;
            },
            $.mage.__('Please enter a valid number in this field.')
        ],
        'validate-number-range': [
            function (v, elm, param) {
                var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;

                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                numValue = $.mage.parseNumber(v);

                if (isNaN(numValue)) {
                    return false;
                }

                dataAttrRange = /^(-?[\d.,]+)?-(-?[\d.,]+)?$/;
                classNameRange = /^number-range-(-?[\d.,]+)?-(-?[\d.,]+)?$/;
                result = true;
                range = param;

                if (typeof range === 'string') {
                    m = dataAttrRange.exec(range);

                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    } else {
                        result = false;
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(' ');
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);

                        if (m) { //eslint-disable-line max-depth
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            $.mage.__('The value is not within the specified range.'),
            true
        ],
        'validate-digits': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || !/[^\d]/.test(v);
            },
            $.mage.__('Please enter a valid number in this field.')
        ],
        'validate-forbidden-extensions': [
            function (v, elem) {
                var forbiddenExtensions = $(elem).attr('data-validation-params'),
                    forbiddenExtensionsArray = forbiddenExtensions.split(','),
                    extensionsArray = v.split(','),
                    result = true;

                this.validateExtensionsMessage = $.mage.__('Forbidden extensions has been used. Avoid usage of ') +
                    forbiddenExtensions;

                $.each(extensionsArray, function (key, extension) {
                    if (forbiddenExtensionsArray.indexOf(extension) !== -1) {
                        result = false;
                    }
                });

                return result;
            }, function () {
                return this.validateExtensionsMessage;
            }
        ],
        'validate-digits-range': [
            function (v, elm, param) {
                var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;

                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                numValue = $.mage.parseNumber(v);

                if (isNaN(numValue)) {
                    return false;
                }

                dataAttrRange = /^(-?\d+)?-(-?\d+)?$/;
                classNameRange = /^digits-range-(-?\d+)?-(-?\d+)?$/;
                result = true;
                range = param;

                if (typeof range === 'string') {
                    m = dataAttrRange.exec(range);

                    if (m) {
                        result = result && $.mage.isBetween(numValue, m[1], m[2]);
                    } else {
                        result = false;
                    }
                } else if (elm && elm.className) {
                    classes = elm.className.split(' ');
                    ii = classes.length;

                    while (ii--) {
                        range = classes[ii];
                        m = classNameRange.exec(range);

                        if (m) { //eslint-disable-line max-depth
                            result = result && $.mage.isBetween(numValue, m[1], m[2]);
                            break;
                        }
                    }
                }

                return result;
            },
            $.mage.__('The value is not within the specified range.'),
            true
        ],
        'validate-range': [
            function (v, elm) {
                var minValue, maxValue, ranges, reRange, result, values,
                    i, name, validRange, minValidRange, maxValidRange;

                if ($.mage.isEmptyNoTrim(v)) {
                    return true;
                } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {
                    minValue = maxValue = $.mage.parseNumber(v);
                } else {
                    ranges = /^(-?\d+)?-(-?\d+)?$/.exec(v);

                    if (ranges) {
                        minValue = $.mage.parseNumber(ranges[1]);
                        maxValue = $.mage.parseNumber(ranges[2]);

                        if (minValue > maxValue) { //eslint-disable-line max-depth
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
                reRange = /^range-(-?\d+)?-(-?\d+)?$/;
                result = true;
                values = $(elm).prop('class').split(' ');

                for (i = values.length - 1; i >= 0; i--) {
                    name = values[i];
                    validRange = reRange.exec(name);

                    if (validRange) {
                        minValidRange = $.mage.parseNumber(validRange[1]);
                        maxValidRange = $.mage.parseNumber(validRange[2]);
                        result = result &&
                            (isNaN(minValidRange) || minValue >= minValidRange) &&
                            (isNaN(maxValidRange) || maxValue <= maxValidRange);
                    }
                }

                return result;
            },
            $.mage.__('The value is not within the specified range.')
        ],
        'validate-alpha': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);
            },
            $.mage.__('Please use letters only (a-z or A-Z) in this field.')
        ],
        'validate-code': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+[a-zA-Z0-9_]+$/.test(v);
            },
            $.mage.__('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.') //eslint-disable-line max-len
        ],
        'validate-alphanum': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);
            },
            $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') //eslint-disable-line max-len
        ],
        'validate-not-number-first': [
            function (value) {
                return $.mage.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value.trim());
            },
            $.mage.__('First character must be letter.')
        ],
        'validate-date': [
            function (value, params, additionalParams) {
                var test = moment(value, utils.convertToMomentFormat(additionalParams.dateFormat));

                return $.mage.isEmptyNoTrim(value) || test.isValid();
            },
            $.mage.__('Please enter a valid date.')

        ],
        'validate-date-range': [
            function (v, elm) {
                var m = /\bdate-range-(\w+)-(\w+)\b/.exec(elm.className),
                    currentYear, normalizedTime, dependentElements;

                if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {
                    return true;
                }

                currentYear = new Date().getFullYear() + '';

                /**
                 * @param {String} vd
                 * @return {Number}
                 */
                normalizedTime = function (vd) {
                    vd = vd.split(/[.\/]/);

                    if (vd[2] && vd[2].length < 4) {
                        vd[2] = currentYear.substr(0, vd[2].length) + vd[2];
                    }

                    return new Date(vd.join('/')).getTime();
                };

                dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');

                return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||
                    normalizedTime(v) <= normalizedTime(dependentElements[0].value);
            },
            $.mage.__('Make sure the To Date is later than or the same as the From Date.')
        ],
        'validate-cpassword': [
            function () {
                var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]),
                    pass = false,
                    passwordElements, i, passwordElement;

                if ($('#password')) {
                    pass = $('#password');
                }
                passwordElements = $('.validate-password');

                for (i = 0; i < passwordElements.length; i++) {
                    passwordElement = $(passwordElements[i]);

                    if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {
                        pass = passwordElement;
                    }
                }

                if ($('.validate-admin-password').length) {
                    pass = $($('.validate-admin-password')[0]);
                }

                return pass.val() === conf.val();
            },
            $.mage.__('Please make sure your passwords match.')
        ],
        'validate-identifier': [
            function (v) {
                return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(v);
            },
            $.mage.__('Please enter a valid URL Key (Ex: "example-page", "example-page.html" or "anotherlevel/example-page").') //eslint-disable-line max-len
        ],
        'validate-zip-international': [

            /*function(v) {
             // @TODO: Cleanup
             return Validation.get('IsEmpty').test(v) ||
             /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
             }*/
            function () {
                return true;
            },
            $.mage.__('Please enter a valid zip code.')
        ],
        'validate-one-required': [
            function (v, elm) {
                var p = $(elm).parent(),
                    options = p.find('input');

                return options.map(function (el) {
                    return $(el).val();
                }).length > 0;
            },
            $.mage.__('Please select one of the options above.')
        ],
        'validate-state': [
            function (v) {
                return v !== 0;
            },
            $.mage.__('Please select State/Province.')
        ],
        'required-file': [
            function (v, elm) {
                var result = !$.mage.isEmptyNoTrim(v),
                    ovId;

                if (!result) {
                    ovId = $('#' + $(elm).attr('id') + '_value');

                    if (ovId.length > 0) {
                        result = !$.mage.isEmptyNoTrim(ovId.val());
                    }
                }

                return result;
            },
            $.mage.__('Please select a file.')
        ],
        'validate-ajax-error': [
            function (v, element) {
                element = $(element);
                element.on('change.ajaxError', function () {
                    element.removeClass('validate-ajax-error');
                    element.off('change.ajaxError');
                });

                return !element.hasClass('validate-ajax-error');
            },
            ''
        ],
        'validate-optional-datetime': [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
                    hasWithValue = false,
                    hasWithNoValue = false,
                    pattern = /day_part$/i,
                    i;

                for (i = 0; i < dateTimeParts.length; i++) {
                    if (!pattern.test($(dateTimeParts[i]).attr('id'))) {
                        if ($(dateTimeParts[i]).val() === 's') { //eslint-disable-line max-depth
                            hasWithValue = true;
                        } else {
                            hasWithNoValue = true;
                        }
                    }
                }

                return hasWithValue ^ hasWithNoValue;
            },
            $.mage.__('The field isn\'t complete.')
        ],
        'validate-required-datetime': [
            function (v, elm, param) {
                var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
                    i;

                for (i = 0; i < dateTimeParts.length; i++) {
                    if (dateTimeParts[i].value === '') {
                        return false;
                    }
                }

                return true;
            },
            $.mage.__('This is a required field.')
        ],
        'validate-one-required-by-name': [
            function (v, elm, selector) {
                var name = elm.name.replace(/([\\"])/g, '\\$1'),
                    container = this.currentForm;

                selector = selector === true ? 'input[name="' + name + '"]:checked' : selector;

                return !!container.querySelectorAll(selector).length;
            },
            $.mage.__('Please select one of the options.')
        ],
        'less-than-equals-to': [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.lteToVal = $(params).val();

                    return parseFloat(value) <= parseFloat($(params).val());
                }

                return true;
            },
            function () {
                var message = $.mage.__('Please enter a value less than or equal to %s.');

                return message.replace('%s', this.lteToVal);
            }
        ],
        'greater-than-equals-to': [
            function (value, element, params) {
                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
                    this.gteToVal = $(params).val();

                    return parseFloat(value) >= parseFloat($(params).val());
                }

                return true;
            },
            function () {
                var message = $.mage.__('Please enter a value greater than or equal to %s.');

                return message.replace('%s', this.gteToVal);
            }
        ],
        'validate-emails': [
            function (value) {
                var validRegexp, emails, i;

                if ($.mage.isEmpty(value)) {
                    return true;
                }
                validRegexp = /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i; //eslint-disable-line max-len
                emails = value.split(/[\s\n\,]+/g);

                for (i = 0; i < emails.length; i++) {
                    if (!validRegexp.test(emails[i].trim())) {
                        return false;
                    }
                }

                return true;
            },
            $.mage.__('Please enter valid email addresses, separated by commas. For example, johndoe@domain.com, johnsmith@domain.com.') //eslint-disable-line max-len
        ],

        'validate-cc-type-select': [

            /**
             * Validate credit card type matches credit card number
             * @param {*} value - select credit card type
             * @param {*} element - element contains the select box for credit card types
             * @param {*} params - selector for credit card number
             * @return {Boolean}
             */
            function (value, element, params) {
                if (value && params && creditCartTypes[value]) {
                    return creditCartTypes[value][0].test($(params).val().replace(/\s+/g, ''));
                }

                return false;
            },
            $.mage.__('Card type does not match credit card number.')
        ],
        'validate-cc-number': [

            /**
             * Validate credit card number based on mod 10.
             *
             * @param {*} value - credit card number
             * @return {Boolean}
             */
            function (value) {
                if (value) {
                    return validateCreditCard(value);
                }

                return false;
            },
            $.mage.__('Please enter a valid credit card number.')
        ],
        'validate-cc-type': [

            /**
             * Validate credit card number is for the correct credit card type.
             *
             * @param {String} value - credit card number
             * @param {*} element - element contains credit card number
             * @param {*} params - selector for credit card type
             * @return {Boolean}
             */
            function (value, element, params) {
                var ccType;

                if (value && params) {
                    ccType = $(params).val();
                    value = value.replace(/\s/g, '').replace(/\-/g, '');

                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][0].test(value);
                    } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {
                        return true;
                    }
                }

                return false;
            },
            $.mage.__('Credit card number does not match credit card type.')
        ],
        'validate-cc-exp': [

            /**
             * Validate credit card expiration date, make sure it's within the year and not before current month.
             *
             * @param {*} value - month
             * @param {*} element - element contains month
             * @param {*} params - year selector
             * @return {Boolean}
             */
            function (value, element, params) {
                var isValid = false,
                    month, year, currentTime, currentMonth, currentYear;

                if (value && params) {
                    month = value;
                    year = $(params).val();
                    currentTime = new Date();
                    currentMonth = currentTime.getMonth() + 1;
                    currentYear = currentTime.getFullYear();

                    isValid = !year || year > currentYear || year == currentYear && month >= currentMonth; //eslint-disable-line
                }

                return isValid;
            },
            $.mage.__('Incorrect credit card expiration date.')
        ],
        'validate-cc-cvn': [

            /**
             * Validate credit card cvn based on credit card type.
             *
             * @param {*} value - credit card cvn
             * @param {*} element - element contains credit card cvn
             * @param {*} params - credit card type selector
             * @return {*}
             */
            function (value, element, params) {
                var ccType;

                if (value && params) {
                    ccType = $(params).val();

                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
                        return creditCartTypes[ccType][1].test(value);
                    }
                }

                return false;
            },
            $.mage.__('Please enter a valid credit card verification number.')
        ],
        'validate-cc-ukss': [

            /**
             * Validate Switch/Solo/Maestro issue number and start date is filled.
             *
             * @param {*} value - input field value
             * @return {*}
             */
            function (value) {
                return value;
            },
            $.mage.__('Please enter issue number or start date for switch/solo card type.')
        ],
        'validate-length': [
            function (v, elm) {
                var reMax = new RegExp(/^maximum-length-[0-9]+$/),
                    reMin = new RegExp(/^minimum-length-[0-9]+$/),
                    validator = this,
                    result = true,
                    length = 0;

                $.each(elm.className.split(' '), function (index, name) {
                    if (name.match(reMax) && result) {
                        length = name.split('-')[2];
                        result = v.length <= length;
                        validator.validateMessage =
                            $.mage.__('Please enter less or equal than %1 symbols.').replace('%1', length);
                    }

                    if (name.match(reMin) && result && !$.mage.isEmpty(v)) {
                        length = name.split('-')[2];
                        result = v.length >= length;
                        validator.validateMessage =
                            $.mage.__('Please enter more or equal than %1 symbols.').replace('%1', length);
                    }
                });

                return result;
            }, function () {
                return this.validateMessage;
            }
        ],
        'required-entry': [
            function (value) {
                return !$.mage.isEmpty(value);
            }, $.mage.__('This is a required field.')
        ],
        'not-negative-amount': [
            function (v) {
                if (v.length) {
                    return (/^\s*\d+([,.]\d+)*\s*%?\s*$/).test(v);
                }

                return true;
            },
            $.mage.__('Please enter positive number in this field.')
        ],
        'validate-per-page-value-list': [
            function (v) {
                var isValid = true,
                    values = v.split(','),
                    i;

                if ($.mage.isEmpty(v)) {
                    return isValid;
                }

                for (i = 0; i < values.length; i++) {
                    if (!/^[0-9]+$/.test(values[i])) {
                        isValid = false;
                    }
                }

                return isValid;
            },
            $.mage.__('Please enter a valid value, ex: 10,20,30')
        ],
        'validate-per-page-value': [
            function (v, elm) {
                var values;

                if ($.mage.isEmpty(v)) {
                    return false;
                }
                values = $('#' + elm.id + '_values').val().split(',');

                return values.indexOf(v) !== -1;
            },
            $.mage.__('Please enter a valid value from list')
        ],
        'validate-new-password': [
            function (v) {
                if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {
                    return false;
                }

                if ($.mage.isEmpty(v) && v !== '') {
                    return false;
                }

                return true;
            },
            $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')
        ],
        'required-if-not-specified': [
            function (value, element, params) {
                var valid = false,
                    alternate = $(params),
                    alternateValue;

                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        alternateValue = alternate.val();

                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line
                            valid = false;
                        }
                    }
                }

                if (!valid) {
                    valid = !this.optional(element);
                }

                return valid;
            },
            $.mage.__('This is a required field.')
        ],
        'required-if-all-sku-empty-and-file-not-loaded': [
            function (value, element, params) {
                var valid = false,
                    alternate = $(params.specifiedId),
                    alternateValue;

                if (alternate.length > 0) {
                    valid = this.check(alternate);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        alternateValue = alternate.val();

                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line
                            valid = false;
                        }
                    }
                }

                if (!valid) {
                    valid = !this.optional(element);
                }

                $('input[' + params.dataSku + '=true]').each(function () {
                    if ($(this).val() !== '') {
                        valid = true;
                    }
                });

                return valid;
            },
            $.mage.__('Please enter valid SKU key.')
        ],
        'required-if-specified': [
            function (value, element, params) {
                var valid = true,
                    dependent = $(params),
                    dependentValue;

                if (dependent.length > 0) {
                    valid = this.check(dependent);
                    // if valid, it may be blank, so check for that
                    if (valid) {
                        dependentValue = dependent.val();
                        valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;
                    }
                }

                if (valid) {
                    valid = !this.optional(element);
                } else {
                    valid = true; // dependent was not valid, so don't even check
                }

                return valid;
            },
            $.mage.__('This is a required field.')
        ],
        'required-number-if-specified': [
            function (value, element, params) {
                var valid = true,
                    dependent = $(params),
                    depeValue;

                if (dependent.length) {
                    valid = this.check(dependent);

                    if (valid) {
                        depeValue = dependent[0].value;
                        valid = !!(depeValue && depeValue.length);
                    }
                }

                return valid ? !!value.length : true;
            },
            $.mage.__('Please enter a valid number.')
        ],
        'datetime-validation': [
            function (value, element) {
                var isValid = true;

                if ($(element).val().length === 0) {
                    isValid = false;
                    $(element).addClass('mage-error');
                }

                return isValid;
            },
            $.mage.__('This is required field')
        ],
        'required-text-swatch-entry': [
            tableSingleValidation,
            $.mage.__('Admin is a required field in each row.')
        ],
        'required-visual-swatch-entry': [
            tableSingleValidation,
            $.mage.__('Admin is a required field in each row.')
        ],
        'required-dropdown-attribute-entry': [
            tableSingleValidation,
            $.mage.__('Admin is a required field in each row.')
        ],
        'validate-item-quantity': [
            function (value, element, params) {
                var validator = this,
                    result = false,
                    // obtain values for validation
                    qty = $.mage.parseNumber(value),
                    isMinAllowedValid = typeof params.minAllowed === 'undefined' ||
                        qty >= $.mage.parseNumber(params.minAllowed),
                    isMaxAllowedValid = typeof params.maxAllowed === 'undefined' ||
                        qty <= $.mage.parseNumber(params.maxAllowed),
                    isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' ||
                        resolveModulo(qty, $.mage.parseNumber(params.qtyIncrements)) === 0.0;

                result = qty > 0;

                if (result === false) {
                    validator.itemQtyErrorMessage = $.mage.__('Please enter a quantity greater than 0.');//eslint-disable-line max-len

                    return result;
                }

                result = isMinAllowedValid;

                if (result === false) {
                    validator.itemQtyErrorMessage = $.mage.__('The fewest you may purchase is %1.').replace('%1', params.minAllowed);//eslint-disable-line max-len

                    return result;
                }

                result = isMaxAllowedValid;

                if (result === false) {
                    validator.itemQtyErrorMessage = $.mage.__('The maximum you may purchase is %1.').replace('%1', params.maxAllowed);//eslint-disable-line max-len

                    return result;
                }

                result = isQtyIncrementsValid;

                if (result === false) {
                    validator.itemQtyErrorMessage = $.mage.__('You can buy this product only in quantities of %1 at a time.').replace('%1', params.qtyIncrements);//eslint-disable-line max-len

                    return result;
                }

                return result;
            }, function () {
                return this.itemQtyErrorMessage;
            }
        ],
        'password-not-equal-to-user-name': [
            function (value, element, params) {
                if (typeof params === 'string') {
                    return value.toLowerCase() !== params.toLowerCase();
                }

                return true;
            },
            $.mage.__('The password can\'t be the same as the email address. Create a new password and try again.')
        ]
    };

    $.each(rules, function (i, rule) {
        rule.unshift(i);
        $.validator.addMethod.apply($.validator, rule);
    });
    $.validator.addClassRules({
        'required-option': {
            required: true
        },
        'required-options-count': {
            required: true
        },
        'validate-both-passwords': {
            'validate-cpassword': true
        }
    });
    $.validator.messages = $.extend($.validator.messages, {
        required: $.mage.__('This is a required field.'),
        remote: $.mage.__('Please fix this field.'),
        email: $.mage.__('Please enter a valid email address.'),
        url: $.mage.__('Please enter a valid URL.'),
        date: $.mage.__('Please enter a valid date.'),
        dateISO: $.mage.__('Please enter a valid date (ISO).'),
        number: $.mage.__('Please enter a valid number.'),
        digits: $.mage.__('Please enter only digits.'),
        creditcard: $.mage.__('Please enter a valid credit card number.'),
        equalTo: $.mage.__('Please enter the same value again.'),
        maxlength: $.validator.format($.mage.__('Please enter no more than {0} characters.')),
        minlength: $.validator.format($.mage.__('Please enter at least {0} characters.')),
        rangelength: $.validator.format($.mage.__('Please enter a value between {0} and {1} characters long.')),
        range: $.validator.format($.mage.__('Please enter a value between {0} and {1}.')),
        max: $.validator.format($.mage.__('Please enter a value less than or equal to {0}.')),
        min: $.validator.format($.mage.__('Please enter a value greater than or equal to {0}.'))
    });

    if ($.metadata) {
        // Setting the type as html5 to enable data-validate attribute
        $.metadata.setType('html5');
    }

    showLabel = $.validator.prototype.showLabel;
    $.extend(true, $.validator.prototype, {
        /**
         * @param {*} element
         * @param {*} message
         */
        showLabel: function (element, message) {
            var label, elem;

            showLabel.call(this, element, message);

            // ARIA (adding aria-invalid & aria-describedby)
            label = this.errorsFor(element);
            elem = $(element);

            if (!label.attr('id')) {
                label.attr('id', this.idOrName(element) + '-error');
            }
            elem.attr('aria-invalid', 'true')
                .attr('aria-describedby', label.attr('id'));
        }
    });

    /**
     * Validate form field without instantiating validate plug-in.
     *
     * @param {Element|String} element - DOM element or selector
     * @return {Boolean} validation result
     */
    $.validator.validateElement = function (element) {
        var form, validator, valid, classes;

        element = $(element);
        form = element.get(0).form;
        validator = form ? $(form).data('validator') : null;

        if (validator) {
            return validator.element(element.get(0));
        }
        valid = true;
        classes = element.prop('class').split(' ');
        $.each(classes, $.proxy(function (i, className) {
            if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
                valid = false;

                return valid;
            }
        }, this));

        return valid;
    };

    originValidateDelegate = $.fn.validateDelegate;

    /**
     * @return {*}
     */
    $.fn.validateDelegate = function () {
        if (!this[0].form) {
            return this;
        }

        return originValidateDelegate.apply(this, arguments);
    };

    /**
     * Validate single element.
     *
     * @param {Element} element
     * @param {Object} config
     * @returns {*}
     */
    $.validator.validateSingleElement = function (element, config) {
        var errors = {},
            valid = true,
            validateConfig = {
                errorElement: 'label',
                ignore: '.ignore-validate',
                hideError: false
            },
            form, validator, classes, elementValue;

        $.extend(validateConfig, config);
        element = $(element).not(validateConfig.ignore);

        if (!element.length) {
            return true;
        }

        form = element.get(0).form;
        validator = form ? $(form).data('validator') : null;

        if (validator) {
            return validator.element(element.get(0));
        }

        classes = element.prop('class').split(' ');
        validator = element.parent().data('validator') ||
            $.mage.validation(validateConfig, element.parent()).validate;

        element.removeClass(validator.settings.errorClass);
        validator.toHide = validator.toShow;
        validator.hideErrors();
        validator.toShow = validator.toHide = $([]);

        $.each(classes, $.proxy(function (i, className) {
            elementValue = element.val();

            if (element.is(':checkbox') || element.is(':radio')) {
                elementValue = element.is(':checked') || null;
            }

            if (this.methods[className] && !this.methods[className](elementValue, element.get(0))) {
                valid = false;
                errors[element.get(0).name] = this.messages[className];
                validator.invalid[element.get(0).name] = true;

                if (!validateConfig.hideError) {
                    validator.showErrors(errors);
                }

                return valid;
            }
        }, this));

        return valid;
    };

    $.widget('mage.validation', {
        options: {
            meta: 'validate',
            onfocusout: false,
            onkeyup: false,
            onclick: false,
            ignoreTitle: true,
            errorClass: 'mage-error',
            errorElement: 'div',

            /**
             * @param {*} error
             * @param {*} element
             */
            errorPlacement: function (error, element) {
                var errorPlacement = element,
                    fieldWrapper;

                // logic for date-picker error placement
                if (element.hasClass('_has-datepicker')) {
                    errorPlacement = element.siblings('button');
                }
                // logic for field wrapper
                fieldWrapper = element.closest('.addon');

                if (fieldWrapper.length) {
                    errorPlacement = fieldWrapper.after(error);
                }
                //logic for checkboxes/radio
                if (element.is(':checkbox') || element.is(':radio')) {
                    errorPlacement = element.parents('.control').children().last();

                    //fallback if group does not have .control parent
                    if (!errorPlacement.length) {
                        errorPlacement = element.siblings('label').last();
                    }
                }
                //logic for control with tooltip
                if (element.siblings('.tooltip').length) {
                    errorPlacement = element.siblings('.tooltip');
                }
                //logic for select with tooltip in after element
                if (element.next().find('.tooltip').length) {
                    errorPlacement = element.next();
                }
                errorPlacement.after(error);
            }
        },

        /**
         * Check if form pass validation rules without submit.
         *
         * @return boolean
         */
        isValid: function () {
            return this.element.valid();
        },

        /**
         * Remove validation error messages
         */
        clearError: function () {
            if (arguments.length) {
                $.each(arguments, $.proxy(function (index, item) {
                    this.validate.prepareElement(item);
                    this.validate.hideErrors();
                }, this));
            } else {
                this.validate.resetForm();
            }
        },

        /**
         * Validation creation.
         *
         * @protected
         */
        _create: function () {
            this.validate = this.element.validate(this.options);

            // ARIA (adding aria-required attribute)
            this.element
                .find('.field.required')
                .find('.control')
                .find('input, select, textarea')
                .attr('aria-required', 'true');

            this._listenFormValidate();
        },

        /**
         * Validation listening.
         *
         * @protected
         */
        _listenFormValidate: function () {
            $('form').on('invalid-form.validate', this.listenFormValidateHandler);
        },

        /**
         * Handle form validation. Focus on first invalid form field.
         *
         * @param {jQuery.Event} event
         * @param {Object} validation
         */
        listenFormValidateHandler: function (event, validation) {
            var firstActive = $(validation.errorList[0].element || []),
                lastActive = $(validation.findLastActive() ||
                    validation.errorList.length && validation.errorList[0].element || []),
                windowHeight = $(window).height(),
                parent, successList;

            if (lastActive.is(':hidden')) {
                parent = lastActive.parent();
                $('html, body').animate({
                    scrollTop: parent.offset().top - windowHeight / 2
                });
            }

            // ARIA (removing aria attributes if success)
            successList = validation.successList;

            if (successList.length) {
                $.each(successList, function () {
                    $(this)
                        .removeAttr('aria-describedby')
                        .removeAttr('aria-invalid');
                });
            }

            if (firstActive.length) {
                $('html, body').stop().animate({
                    scrollTop: firstActive.parent().offset().top - windowHeight / 2
                });
                firstActive.focus();
            }
        }
    });

    return $.mage.validation;
});

/* eslint-disable max-len, camelcase */
/*!
 * jQuery UI Datepicker 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Datepicker
//>>group: Widgets
//>>description: Displays a calendar from an input or inline for selecting dates.
//>>docs: http://api.jqueryui.com/datepicker/
//>>demos: http://jqueryui.com/datepicker/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/datepicker.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

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

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

    $.extend( $.ui, { datepicker: { version: "1.13.2" } } );

    var datepicker_instActive;

    function datepicker_getZindex( elem ) {
        var 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;
    }

    /* Date picker manager.
       Use the singleton instance of this class, $.datepicker, to interact with the date picker.
       Settings for (groups of) date pickers are maintained in an instance object,
       allowing multiple different settings on the same page. */

    function Datepicker() {
        this._curInst = null; // The current instance in use
        this._keyEvent = false; // If the last event was a key event
        this._disabledInputs = []; // List of date picker inputs that have been disabled
        this._datepickerShowing = false; // True if the popup picker is showing , false if not
        this._inDialog = false; // True if showing within a "dialog", false if not
        this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
        this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
        this._appendClass = "ui-datepicker-append"; // The name of the append marker class
        this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
        this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
        this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
        this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
        this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
        this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
        this.regional = []; // Available regional settings, indexed by language code
        this.regional[ "" ] = { // Default regional settings
            closeText: "Done", // Display text for close link
            prevText: "Prev", // Display text for previous month link
            nextText: "Next", // Display text for next month link
            currentText: "Today", // Display text for current month link
            monthNames: [ "January", "February", "March", "April", "May", "June",
                "July", "August", "September", "October", "November", "December" ], // Names of months for drop-down and formatting
            monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
            dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
            dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
            dayNamesMin: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ], // Column headings for days starting at Sunday
            weekHeader: "Wk", // Column header for week of the year
            dateFormat: "mm/dd/yy", // See format options on parseDate
            firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
            isRTL: false, // True if right-to-left language, false if left-to-right
            showMonthAfterYear: false, // True if the year select precedes month, false for month then year
            yearSuffix: "", // Additional text to append to the year in the month headers,
            selectMonthLabel: "Select month", // Invisible label for month selector
            selectYearLabel: "Select year" // Invisible label for year selector
        };
        this._defaults = { // Global defaults for all the date picker instances
            showOn: "focus", // "focus" for popup on focus,
            // "button" for trigger button, or "both" for either
            showAnim: "fadeIn", // Name of jQuery animation for popup
            showOptions: {}, // Options for enhanced animations
            defaultDate: null, // Used when field is blank: actual date,
            // +/-number for offset from today, null for today
            appendText: "", // Display text following the input box, e.g. showing the format
            buttonText: "...", // Text for trigger button
            buttonImage: "", // URL for trigger button image
            buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
            hideIfNoPrevNext: false, // True to hide next/previous month links
            // if not applicable, false to just disable them
            navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
            gotoCurrent: false, // True if today link goes back to current selection instead
            changeMonth: false, // True if month can be selected directly, false if only prev/next
            changeYear: false, // True if year can be selected directly, false if only prev/next
            yearRange: "c-10:c+10", // Range of years to display in drop-down,
            // either relative to today's year (-nn:+nn), relative to currently displayed year
            // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
            showOtherMonths: false, // True to show dates in other months, false to leave blank
            selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
            showWeek: false, // True to show week of the year, false to not show it
            calculateWeek: this.iso8601Week, // How to calculate the week of the year,
            // takes a Date and returns the number of the week for it
            shortYearCutoff: "+10", // Short year values < this are in the current century,
            // > this are in the previous century,
            // string value starting with "+" for current year + value
            minDate: null, // The earliest selectable date, or null for no limit
            maxDate: null, // The latest selectable date, or null for no limit
            duration: "fast", // Duration of display/closure
            beforeShowDay: null, // Function that takes a date and returns an array with
            // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
            // [2] = cell title (optional), e.g. $.datepicker.noWeekends
            beforeShow: null, // Function that takes an input field and
            // returns a set of custom settings for the date picker
            onSelect: null, // Define a callback function when a date is selected
            onChangeMonthYear: null, // Define a callback function when the month or year is changed
            onClose: null, // Define a callback function when the datepicker is closed
            onUpdateDatepicker: null, // Define a callback function when the datepicker is updated
            numberOfMonths: 1, // Number of months to show at a time
            showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
            stepMonths: 1, // Number of months to step back/forward
            stepBigMonths: 12, // Number of months to step back/forward for the big links
            altField: "", // Selector for an alternate field to store selected dates into
            altFormat: "", // The date format to use for the alternate field
            constrainInput: true, // The input is constrained by the current date format
            showButtonPanel: false, // True to show button panel, false to not show it
            autoSize: false, // True to size the input for the date format, false to leave as is
            disabled: false // The initial disabled state
        };
        $.extend( this._defaults, this.regional[ "" ] );
        this.regional.en = $.extend( true, {}, this.regional[ "" ] );
        this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
        this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
    }

    $.extend( Datepicker.prototype, {

        /* Class name added to elements to indicate already configured with a date picker. */
        markerClassName: "hasDatepicker",

        //Keep track of the maximum number of rows displayed (see #7043)
        maxRows: 4,

        // TODO rename to "widget" when switching to widget factory
        _widgetDatepicker: function() {
            return this.dpDiv;
        },

        /* Override the default settings for all instances of the date picker.
         * @param  settings  object - the new settings to use as defaults (anonymous object)
         * @return the manager object
         */
        setDefaults: function( settings ) {
            datepicker_extendRemove( this._defaults, settings || {} );
            return this;
        },

        /* Attach the date picker to a jQuery selection.
         * @param  target	element - the target input field or division or span
         * @param  settings  object - the new settings to use for this date picker instance (anonymous)
         */
        _attachDatepicker: function( target, settings ) {
            var nodeName, inline, inst;
            nodeName = target.nodeName.toLowerCase();
            inline = ( nodeName === "div" || nodeName === "span" );
            if ( !target.id ) {
                this.uuid += 1;
                target.id = "dp" + this.uuid;
            }
            inst = this._newInst( $( target ), inline );
            inst.settings = $.extend( {}, settings || {} );
            if ( nodeName === "input" ) {
                this._connectDatepicker( target, inst );
            } else if ( inline ) {
                this._inlineDatepicker( target, inst );
            }
        },

        /* Create a new instance object. */
        _newInst: function( target, inline ) {
            var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
            return { id: id, input: target, // associated target
                selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
                drawMonth: 0, drawYear: 0, // month being drawn
                inline: inline, // is datepicker inline or not
                dpDiv: ( !inline ? this.dpDiv : // presentation div
                    datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
        },

        /* Attach the date picker to an input field. */
        _connectDatepicker: function( target, inst ) {
            var input = $( target );
            inst.append = $( [] );
            inst.trigger = $( [] );
            if ( input.hasClass( this.markerClassName ) ) {
                return;
            }
            this._attachments( input, inst );
            input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
            on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
            this._autoSize( inst );
            $.data( target, "datepicker", inst );

            //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
            if ( inst.settings.disabled ) {
                this._disableDatepicker( target );
            }
        },

        /* Make attachments based on settings. */
        _attachments: function( input, inst ) {
            var showOn, buttonText, buttonImage,
                appendText = this._get( inst, "appendText" ),
                isRTL = this._get( inst, "isRTL" );

            if ( inst.append ) {
                inst.append.remove();
            }
            if ( appendText ) {
                inst.append = $( "<span>" )
                    .addClass( this._appendClass )
                    .text( appendText );
                input[ isRTL ? "before" : "after" ]( inst.append );
            }

            input.off( "focus", this._showDatepicker );

            if ( inst.trigger ) {
                inst.trigger.remove();
            }

            showOn = this._get( inst, "showOn" );
            if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
                input.on( "focus", this._showDatepicker );
            }
            if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
                buttonText = this._get( inst, "buttonText" );
                buttonImage = this._get( inst, "buttonImage" );

                if ( this._get( inst, "buttonImageOnly" ) ) {
                    inst.trigger = $( "<img>" )
                        .addClass( this._triggerClass )
                        .attr( {
                            src: buttonImage,
                            alt: buttonText,
                            title: buttonText
                        } );
                } else {
                    inst.trigger = $( "<button type='button'>" )
                        .addClass( this._triggerClass );
                    if ( buttonImage ) {
                        inst.trigger.html(
                            $( "<img>" )
                                .attr( {
                                    src: buttonImage,
                                    alt: buttonText,
                                    title: buttonText
                                } )
                        );
                    } else {
                        inst.trigger.text( buttonText );
                    }
                }

                input[ isRTL ? "before" : "after" ]( inst.trigger );
                inst.trigger.on( "click", function() {
                    if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
                        $.datepicker._hideDatepicker();
                    } else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
                        $.datepicker._hideDatepicker();
                        $.datepicker._showDatepicker( input[ 0 ] );
                    } else {
                        $.datepicker._showDatepicker( input[ 0 ] );
                    }
                    return false;
                } );
            }
        },

        /* Apply the maximum length for the date format. */
        _autoSize: function( inst ) {
            if ( this._get( inst, "autoSize" ) && !inst.inline ) {
                var findMax, max, maxI, i,
                    date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
                    dateFormat = this._get( inst, "dateFormat" );

                if ( dateFormat.match( /[DM]/ ) ) {
                    findMax = function( names ) {
                        max = 0;
                        maxI = 0;
                        for ( i = 0; i < names.length; i++ ) {
                            if ( names[ i ].length > max ) {
                                max = names[ i ].length;
                                maxI = i;
                            }
                        }
                        return maxI;
                    };
                    date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
                        "monthNames" : "monthNamesShort" ) ) ) );
                    date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
                        "dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
                }
                inst.input.attr( "size", this._formatDate( inst, date ).length );
            }
        },

        /* Attach an inline date picker to a div. */
        _inlineDatepicker: function( target, inst ) {
            var divSpan = $( target );
            if ( divSpan.hasClass( this.markerClassName ) ) {
                return;
            }
            divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
            $.data( target, "datepicker", inst );
            this._setDate( inst, this._getDefaultDate( inst ), true );
            this._updateDatepicker( inst );
            this._updateAlternate( inst );

            //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
            if ( inst.settings.disabled ) {
                this._disableDatepicker( target );
            }

            // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
            // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
            inst.dpDiv.css( "display", "block" );
        },

        /* Pop-up the date picker in a "dialog" box.
         * @param  input element - ignored
         * @param  date	string or Date - the initial date to display
         * @param  onSelect  function - the function to call when a date is selected
         * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
         * @param  pos int[2] - coordinates for the dialog's position within the screen or
         *					event - with x/y coordinates or
         *					leave empty for default (screen centre)
         * @return the manager object
         */
        _dialogDatepicker: function( input, date, onSelect, settings, pos ) {
            var id, browserWidth, browserHeight, scrollX, scrollY,
                inst = this._dialogInst; // internal instance

            if ( !inst ) {
                this.uuid += 1;
                id = "dp" + this.uuid;
                this._dialogInput = $( "<input type='text' id='" + id +
                    "' style='position: absolute; top: -100px; width: 0px;'/>" );
                this._dialogInput.on( "keydown", this._doKeyDown );
                $( "body" ).append( this._dialogInput );
                inst = this._dialogInst = this._newInst( this._dialogInput, false );
                inst.settings = {};
                $.data( this._dialogInput[ 0 ], "datepicker", inst );
            }
            datepicker_extendRemove( inst.settings, settings || {} );
            date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
            this._dialogInput.val( date );

            this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
            if ( !this._pos ) {
                browserWidth = document.documentElement.clientWidth;
                browserHeight = document.documentElement.clientHeight;
                scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
                scrollY = document.documentElement.scrollTop || document.body.scrollTop;
                this._pos = // should use actual width/height below
                    [ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
            }

            // Move input on screen for focus, but hidden behind dialog
            this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
            inst.settings.onSelect = onSelect;
            this._inDialog = true;
            this.dpDiv.addClass( this._dialogClass );
            this._showDatepicker( this._dialogInput[ 0 ] );
            if ( $.blockUI ) {
                $.blockUI( this.dpDiv );
            }
            $.data( this._dialogInput[ 0 ], "datepicker", inst );
            return this;
        },

        /* Detach a datepicker from its control.
         * @param  target	element - the target input field or division or span
         */
        _destroyDatepicker: function( target ) {
            var nodeName,
                $target = $( target ),
                inst = $.data( target, "datepicker" );

            if ( !$target.hasClass( this.markerClassName ) ) {
                return;
            }

            nodeName = target.nodeName.toLowerCase();
            $.removeData( target, "datepicker" );
            if ( nodeName === "input" ) {
                inst.append.remove();
                inst.trigger.remove();
                $target.removeClass( this.markerClassName ).
                off( "focus", this._showDatepicker ).
                off( "keydown", this._doKeyDown ).
                off( "keypress", this._doKeyPress ).
                off( "keyup", this._doKeyUp );
            } else if ( nodeName === "div" || nodeName === "span" ) {
                $target.removeClass( this.markerClassName ).empty();
            }

            if ( datepicker_instActive === inst ) {
                datepicker_instActive = null;
                this._curInst = null;
            }
        },

        /* Enable the date picker to a jQuery selection.
         * @param  target	element - the target input field or division or span
         */
        _enableDatepicker: function( target ) {
            var nodeName, inline,
                $target = $( target ),
                inst = $.data( target, "datepicker" );

            if ( !$target.hasClass( this.markerClassName ) ) {
                return;
            }

            nodeName = target.nodeName.toLowerCase();
            if ( nodeName === "input" ) {
                target.disabled = false;
                inst.trigger.filter( "button" ).
                each( function() {
                    this.disabled = false;
                } ).end().
                filter( "img" ).css( { opacity: "1.0", cursor: "" } );
            } else if ( nodeName === "div" || nodeName === "span" ) {
                inline = $target.children( "." + this._inlineClass );
                inline.children().removeClass( "ui-state-disabled" );
                inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
                prop( "disabled", false );
            }
            this._disabledInputs = $.map( this._disabledInputs,

                // Delete entry
                function( value ) {
                    return ( value === target ? null : value );
                } );
        },

        /* Disable the date picker to a jQuery selection.
         * @param  target	element - the target input field or division or span
         */
        _disableDatepicker: function( target ) {
            var nodeName, inline,
                $target = $( target ),
                inst = $.data( target, "datepicker" );

            if ( !$target.hasClass( this.markerClassName ) ) {
                return;
            }

            nodeName = target.nodeName.toLowerCase();
            if ( nodeName === "input" ) {
                target.disabled = true;
                inst.trigger.filter( "button" ).
                each( function() {
                    this.disabled = true;
                } ).end().
                filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
            } else if ( nodeName === "div" || nodeName === "span" ) {
                inline = $target.children( "." + this._inlineClass );
                inline.children().addClass( "ui-state-disabled" );
                inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
                prop( "disabled", true );
            }
            this._disabledInputs = $.map( this._disabledInputs,

                // Delete entry
                function( value ) {
                    return ( value === target ? null : value );
                } );
            this._disabledInputs[ this._disabledInputs.length ] = target;
        },

        /* Is the first field in a jQuery collection disabled as a datepicker?
         * @param  target	element - the target input field or division or span
         * @return boolean - true if disabled, false if enabled
         */
        _isDisabledDatepicker: function( target ) {
            if ( !target ) {
                return false;
            }
            for ( var i = 0; i < this._disabledInputs.length; i++ ) {
                if ( this._disabledInputs[ i ] === target ) {
                    return true;
                }
            }
            return false;
        },

        /* Retrieve the instance data for the target control.
         * @param  target  element - the target input field or division or span
         * @return  object - the associated instance data
         * @throws  error if a jQuery problem getting data
         */
        _getInst: function( target ) {
            try {
                return $.data( target, "datepicker" );
            } catch ( err ) {
                throw "Missing instance data for this datepicker";
            }
        },

        /* Update or retrieve the settings for a date picker attached to an input field or division.
         * @param  target  element - the target input field or division or span
         * @param  name	object - the new settings to update or
         *				string - the name of the setting to change or retrieve,
         *				when retrieving also "all" for all instance settings or
         *				"defaults" for all global defaults
         * @param  value   any - the new value for the setting
         *				(omit if above is an object or to retrieve a value)
         */
        _optionDatepicker: function( target, name, value ) {
            var settings, date, minDate, maxDate,
                inst = this._getInst( target );

            if ( arguments.length === 2 && typeof name === "string" ) {
                return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
                    ( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
                        this._get( inst, name ) ) : null ) );
            }

            settings = name || {};
            if ( typeof name === "string" ) {
                settings = {};
                settings[ name ] = value;
            }

            if ( inst ) {
                if ( this._curInst === inst ) {
                    this._hideDatepicker();
                }

                date = this._getDateDatepicker( target, true );
                minDate = this._getMinMaxDate( inst, "min" );
                maxDate = this._getMinMaxDate( inst, "max" );
                datepicker_extendRemove( inst.settings, settings );

                // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
                if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
                    inst.settings.minDate = this._formatDate( inst, minDate );
                }
                if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
                    inst.settings.maxDate = this._formatDate( inst, maxDate );
                }
                if ( "disabled" in settings ) {
                    if ( settings.disabled ) {
                        this._disableDatepicker( target );
                    } else {
                        this._enableDatepicker( target );
                    }
                }
                this._attachments( $( target ), inst );
                this._autoSize( inst );
                this._setDate( inst, date );
                this._updateAlternate( inst );
                this._updateDatepicker( inst );
            }
        },

        // Change method deprecated
        _changeDatepicker: function( target, name, value ) {
            this._optionDatepicker( target, name, value );
        },

        /* Redraw the date picker attached to an input field or division.
         * @param  target  element - the target input field or division or span
         */
        _refreshDatepicker: function( target ) {
            var inst = this._getInst( target );
            if ( inst ) {
                this._updateDatepicker( inst );
            }
        },

        /* Set the dates for a jQuery selection.
         * @param  target element - the target input field or division or span
         * @param  date	Date - the new date
         */
        _setDateDatepicker: function( target, date ) {
            var inst = this._getInst( target );
            if ( inst ) {
                this._setDate( inst, date );
                this._updateDatepicker( inst );
                this._updateAlternate( inst );
            }
        },

        /* Get the date(s) for the first entry in a jQuery selection.
         * @param  target element - the target input field or division or span
         * @param  noDefault boolean - true if no default date is to be used
         * @return Date - the current date
         */
        _getDateDatepicker: function( target, noDefault ) {
            var inst = this._getInst( target );
            if ( inst && !inst.inline ) {
                this._setDateFromField( inst, noDefault );
            }
            return ( inst ? this._getDate( inst ) : null );
        },

        /* Handle keystrokes. */
        _doKeyDown: function( event ) {
            var onSelect, dateStr, sel,
                inst = $.datepicker._getInst( event.target ),
                handled = true,
                isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );

            inst._keyEvent = true;
            if ( $.datepicker._datepickerShowing ) {
                switch ( event.keyCode ) {
                    case 9: $.datepicker._hideDatepicker();
                        handled = false;
                        break; // hide on tab out
                    case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
                        $.datepicker._currentClass + ")", inst.dpDiv );
                        if ( sel[ 0 ] ) {
                            $.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
                        }

                        onSelect = $.datepicker._get( inst, "onSelect" );
                        if ( onSelect ) {
                            dateStr = $.datepicker._formatDate( inst );

                            // Trigger custom callback
                            onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
                        } else {
                            $.datepicker._hideDatepicker();
                        }

                        return false; // don't submit the form
                    case 27: $.datepicker._hideDatepicker();
                        break; // hide on escape
                    case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
                        -$.datepicker._get( inst, "stepBigMonths" ) :
                        -$.datepicker._get( inst, "stepMonths" ) ), "M" );
                        break; // previous month/year on page up/+ ctrl
                    case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
                        +$.datepicker._get( inst, "stepBigMonths" ) :
                        +$.datepicker._get( inst, "stepMonths" ) ), "M" );
                        break; // next month/year on page down/+ ctrl
                    case 35: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._clearDate( event.target );
                    }
                        handled = event.ctrlKey || event.metaKey;
                        break; // clear on ctrl or command +end
                    case 36: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._gotoToday( event.target );
                    }
                        handled = event.ctrlKey || event.metaKey;
                        break; // current on ctrl or command +home
                    case 37: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
                    }
                        handled = event.ctrlKey || event.metaKey;

                        // -1 day on ctrl or command +left
                        if ( event.originalEvent.altKey ) {
                            $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
                                -$.datepicker._get( inst, "stepBigMonths" ) :
                                -$.datepicker._get( inst, "stepMonths" ) ), "M" );
                        }

                        // next month/year on alt +left on Mac
                        break;
                    case 38: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._adjustDate( event.target, -7, "D" );
                    }
                        handled = event.ctrlKey || event.metaKey;
                        break; // -1 week on ctrl or command +up
                    case 39: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
                    }
                        handled = event.ctrlKey || event.metaKey;

                        // +1 day on ctrl or command +right
                        if ( event.originalEvent.altKey ) {
                            $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
                                +$.datepicker._get( inst, "stepBigMonths" ) :
                                +$.datepicker._get( inst, "stepMonths" ) ), "M" );
                        }

                        // next month/year on alt +right
                        break;
                    case 40: if ( event.ctrlKey || event.metaKey ) {
                        $.datepicker._adjustDate( event.target, +7, "D" );
                    }
                        handled = event.ctrlKey || event.metaKey;
                        break; // +1 week on ctrl or command +down
                    default: handled = false;
                }
            } else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
                $.datepicker._showDatepicker( this );
            } else {
                handled = false;
            }

            if ( handled ) {
                event.preventDefault();
                event.stopPropagation();
            }
        },

        /* Filter entered characters - based on date format. */
        _doKeyPress: function( event ) {
            var chars, chr,
                inst = $.datepicker._getInst( event.target );

            if ( $.datepicker._get( inst, "constrainInput" ) ) {
                chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
                chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
                return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
            }
        },

        /* Synchronise manual entry and field/alternate field. */
        _doKeyUp: function( event ) {
            var date,
                inst = $.datepicker._getInst( event.target );

            if ( inst.input.val() !== inst.lastVal ) {
                try {
                    date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
                        ( inst.input ? inst.input.val() : null ),
                        $.datepicker._getFormatConfig( inst ) );

                    if ( date ) { // only if valid
                        $.datepicker._setDateFromField( inst );
                        $.datepicker._updateAlternate( inst );
                        $.datepicker._updateDatepicker( inst );
                    }
                } catch ( err ) {
                }
            }
            return true;
        },

        /* Pop-up the date picker for a given input field.
         * If false returned from beforeShow event handler do not show.
         * @param  input  element - the input field attached to the date picker or
         *					event - if triggered by focus
         */
        _showDatepicker: function( input ) {
            input = input.target || input;
            if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
                input = $( "input", input.parentNode )[ 0 ];
            }

            if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
                return;
            }

            var inst, beforeShow, beforeShowSettings, isFixed,
                offset, showAnim, duration;

            inst = $.datepicker._getInst( input );
            if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
                $.datepicker._curInst.dpDiv.stop( true, true );
                if ( inst && $.datepicker._datepickerShowing ) {
                    $.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
                }
            }

            beforeShow = $.datepicker._get( inst, "beforeShow" );
            beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
            if ( beforeShowSettings === false ) {
                return;
            }
            datepicker_extendRemove( inst.settings, beforeShowSettings );

            inst.lastVal = null;
            $.datepicker._lastInput = input;
            $.datepicker._setDateFromField( inst );

            if ( $.datepicker._inDialog ) { // hide cursor
                input.value = "";
            }
            if ( !$.datepicker._pos ) { // position below input
                $.datepicker._pos = $.datepicker._findPos( input );
                $.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
            }

            isFixed = false;
            $( input ).parents().each( function() {
                isFixed |= $( this ).css( "position" ) === "fixed";
                return !isFixed;
            } );

            offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
            $.datepicker._pos = null;

            //to avoid flashes on Firefox
            inst.dpDiv.empty();

            // determine sizing offscreen
            inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
            $.datepicker._updateDatepicker( inst );

            // fix width for dynamic number of date pickers
            // and adjust position before showing
            offset = $.datepicker._checkOffset( inst, offset, isFixed );
            inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
                    "static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
                left: offset.left + "px", top: offset.top + "px" } );

            if ( !inst.inline ) {
                showAnim = $.datepicker._get( inst, "showAnim" );
                duration = $.datepicker._get( inst, "duration" );
                inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
                $.datepicker._datepickerShowing = true;

                if ( $.effects && $.effects.effect[ showAnim ] ) {
                    inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
                } else {
                    inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
                }

                if ( $.datepicker._shouldFocusInput( inst ) ) {
                    inst.input.trigger( "focus" );
                }

                $.datepicker._curInst = inst;
            }
        },

        /* Generate the date picker content. */
        _updateDatepicker: function( inst ) {
            this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
            datepicker_instActive = inst; // for delegate hover events
            inst.dpDiv.empty().append( this._generateHTML( inst ) );
            this._attachHandlers( inst );

            var origyearshtml,
                numMonths = this._getNumberOfMonths( inst ),
                cols = numMonths[ 1 ],
                width = 17,
                activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" ),
                onUpdateDatepicker = $.datepicker._get( inst, "onUpdateDatepicker" );

            if ( activeCell.length > 0 ) {
                datepicker_handleMouseover.apply( activeCell.get( 0 ) );
            }

            inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
            if ( cols > 1 ) {
                inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
            }
            inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
            "Class" ]( "ui-datepicker-multi" );
            inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
            "Class" ]( "ui-datepicker-rtl" );

            if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
                inst.input.trigger( "focus" );
            }

            // Deffered render of the years select (to avoid flashes on Firefox)
            if ( inst.yearshtml ) {
                origyearshtml = inst.yearshtml;
                setTimeout( function() {

                    //assure that inst.yearshtml didn't change.
                    if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
                        inst.dpDiv.find( "select.ui-datepicker-year" ).first().replaceWith( inst.yearshtml );
                    }
                    origyearshtml = inst.yearshtml = null;
                }, 0 );
            }

            if ( onUpdateDatepicker ) {
                onUpdateDatepicker.apply( ( inst.input ? inst.input[ 0 ] : null ), [ inst ] );
            }
        },

        // #6694 - don't focus the input if it's already focused
        // this breaks the change event in IE
        // Support: IE and jQuery <1.9
        _shouldFocusInput: function( inst ) {
            return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
        },

        /* Check positioning to remain on screen. */
        _checkOffset: function( inst, offset, isFixed ) {
            var dpWidth = inst.dpDiv.outerWidth(),
                dpHeight = inst.dpDiv.outerHeight(),
                inputWidth = inst.input ? inst.input.outerWidth() : 0,
                inputHeight = inst.input ? inst.input.outerHeight() : 0,
                viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
                viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );

            offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
            offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
            offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;

            // Now check if datepicker is showing outside window viewport - move to a better place if so.
            offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
                Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
            offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
                Math.abs( dpHeight + inputHeight ) : 0 );

            return offset;
        },

        /* Find an object's position on the screen. */
        _findPos: function( obj ) {
            var position,
                inst = this._getInst( obj ),
                isRTL = this._get( inst, "isRTL" );

            while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.pseudos.hidden( obj ) ) ) {
                obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
            }

            position = $( obj ).offset();
            return [ position.left, position.top ];
        },

        /* Hide the date picker from view.
         * @param  input  element - the input field attached to the date picker
         */
        _hideDatepicker: function( input ) {
            var showAnim, duration, postProcess, onClose,
                inst = this._curInst;

            if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
                return;
            }

            if ( this._datepickerShowing ) {
                showAnim = this._get( inst, "showAnim" );
                duration = this._get( inst, "duration" );
                postProcess = function() {
                    $.datepicker._tidyDialog( inst );
                };

                // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
                if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
                    inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
                } else {
                    inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
                        ( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
                }

                if ( !showAnim ) {
                    postProcess();
                }
                this._datepickerShowing = false;

                onClose = this._get( inst, "onClose" );
                if ( onClose ) {
                    onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
                }

                this._lastInput = null;
                if ( this._inDialog ) {
                    this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
                    if ( $.blockUI ) {
                        $.unblockUI();
                        $( "body" ).append( this.dpDiv );
                    }
                }
                this._inDialog = false;
            }
        },

        /* Tidy up after a dialog display. */
        _tidyDialog: function( inst ) {
            inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
        },

        /* Close date picker if clicked elsewhere. */
        _checkExternalClick: function( event ) {
            if ( !$.datepicker._curInst ) {
                return;
            }

            var $target = $( event.target ),
                inst = $.datepicker._getInst( $target[ 0 ] );

            if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
                    $target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
                    !$target.hasClass( $.datepicker.markerClassName ) &&
                    !$target.closest( "." + $.datepicker._triggerClass ).length &&
                    $.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
                ( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
                $.datepicker._hideDatepicker();
            }
        },

        /* Adjust one of the date sub-fields. */
        _adjustDate: function( id, offset, period ) {
            var target = $( id ),
                inst = this._getInst( target[ 0 ] );

            if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
                return;
            }
            this._adjustInstDate( inst, offset, period );
            this._updateDatepicker( inst );
        },

        /* Action for current link. */
        _gotoToday: function( id ) {
            var date,
                target = $( id ),
                inst = this._getInst( target[ 0 ] );

            if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
                inst.selectedDay = inst.currentDay;
                inst.drawMonth = inst.selectedMonth = inst.currentMonth;
                inst.drawYear = inst.selectedYear = inst.currentYear;
            } else {
                date = new Date();
                inst.selectedDay = date.getDate();
                inst.drawMonth = inst.selectedMonth = date.getMonth();
                inst.drawYear = inst.selectedYear = date.getFullYear();
            }
            this._notifyChange( inst );
            this._adjustDate( target );
        },

        /* Action for selecting a new month/year. */
        _selectMonthYear: function( id, select, period ) {
            var target = $( id ),
                inst = this._getInst( target[ 0 ] );

            inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
                inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
                    parseInt( select.options[ select.selectedIndex ].value, 10 );

            this._notifyChange( inst );
            this._adjustDate( target );
        },

        /* Action for selecting a day. */
        _selectDay: function( id, month, year, td ) {
            var inst,
                target = $( id );

            if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
                return;
            }

            inst = this._getInst( target[ 0 ] );
            inst.selectedDay = inst.currentDay = parseInt( $( "a", td ).attr( "data-date" ) );
            inst.selectedMonth = inst.currentMonth = month;
            inst.selectedYear = inst.currentYear = year;
            this._selectDate( id, this._formatDate( inst,
                inst.currentDay, inst.currentMonth, inst.currentYear ) );
        },

        /* Erase the input field and hide the date picker. */
        _clearDate: function( id ) {
            var target = $( id );
            this._selectDate( target, "" );
        },

        /* Update the input field with the selected date. */
        _selectDate: function( id, dateStr ) {
            var onSelect,
                target = $( id ),
                inst = this._getInst( target[ 0 ] );

            dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
            if ( inst.input ) {
                inst.input.val( dateStr );
            }
            this._updateAlternate( inst );

            onSelect = this._get( inst, "onSelect" );
            if ( onSelect ) {
                onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );  // trigger custom callback
            } else if ( inst.input ) {
                inst.input.trigger( "change" ); // fire the change event
            }

            if ( inst.inline ) {
                this._updateDatepicker( inst );
            } else {
                this._hideDatepicker();
                this._lastInput = inst.input[ 0 ];
                if ( typeof( inst.input[ 0 ] ) !== "object" ) {
                    inst.input.trigger( "focus" ); // restore focus
                }
                this._lastInput = null;
            }
        },

        /* Update any alternate field to synchronise with the main field. */
        _updateAlternate: function( inst ) {
            var altFormat, date, dateStr,
                altField = this._get( inst, "altField" );

            if ( altField ) { // update alternate field too
                altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
                date = this._getDate( inst );
                dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
                $( document ).find( altField ).val( dateStr );
            }
        },

        /* Set as beforeShowDay function to prevent selection of weekends.
         * @param  date  Date - the date to customise
         * @return [boolean, string] - is this date selectable?, what is its CSS class?
         */
        noWeekends: function( date ) {
            var day = date.getDay();
            return [ ( day > 0 && day < 6 ), "" ];
        },

        /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
         * @param  date  Date - the date to get the week for
         * @return  number - the number of the week within the year that contains this date
         */
        iso8601Week: function( date ) {
            var time,
                checkDate = new Date( date.getTime() );

            // Find Thursday of this week starting on Monday
            checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );

            time = checkDate.getTime();
            checkDate.setMonth( 0 ); // Compare with Jan 1
            checkDate.setDate( 1 );
            return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
        },

        /* Parse a string value into a date object.
         * See formatDate below for the possible formats.
         *
         * @param  format string - the expected format of the date
         * @param  value string - the date in the above format
         * @param  settings Object - attributes include:
         *					shortYearCutoff  number - the cutoff year for determining the century (optional)
         *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
         *					dayNames		string[7] - names of the days from Sunday (optional)
         *					monthNamesShort string[12] - abbreviated names of the months (optional)
         *					monthNames		string[12] - names of the months (optional)
         * @return  Date - the extracted date value or null if value is blank
         */
        parseDate: function( format, value, settings ) {
            if ( format == null || value == null ) {
                throw "Invalid arguments";
            }

            value = ( typeof value === "object" ? value.toString() : value + "" );
            if ( value === "" ) {
                return null;
            }

            var iFormat, dim, extra,
                iValue = 0,
                shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
                shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
                    new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
                dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
                dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
                monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
                monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
                year = -1,
                month = -1,
                day = -1,
                doy = -1,
                literal = false,
                date,

                // Check whether a format character is doubled
                lookAhead = function( match ) {
                    var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
                    if ( matches ) {
                        iFormat++;
                    }
                    return matches;
                },

                // Extract a number from the string value
                getNumber = function( match ) {
                    var isDoubled = lookAhead( match ),
                        size = ( match === "@" ? 14 : ( match === "!" ? 20 :
                            ( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
                        minSize = ( match === "y" ? size : 1 ),
                        digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
                        num = value.substring( iValue ).match( digits );
                    if ( !num ) {
                        throw "Missing number at position " + iValue;
                    }
                    iValue += num[ 0 ].length;
                    return parseInt( num[ 0 ], 10 );
                },

                // Extract a name from the string value and convert to an index
                getName = function( match, shortNames, longNames ) {
                    var index = -1,
                        names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
                            return [ [ k, v ] ];
                        } ).sort( function( a, b ) {
                            return -( a[ 1 ].length - b[ 1 ].length );
                        } );

                    $.each( names, function( i, pair ) {
                        var name = pair[ 1 ];
                        if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
                            index = pair[ 0 ];
                            iValue += name.length;
                            return false;
                        }
                    } );
                    if ( index !== -1 ) {
                        return index + 1;
                    } else {
                        throw "Unknown name at position " + iValue;
                    }
                },

                // Confirm that a literal character matches the string value
                checkLiteral = function() {
                    if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
                        throw "Unexpected literal at position " + iValue;
                    }
                    iValue++;
                };

            for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
                if ( literal ) {
                    if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
                        literal = false;
                    } else {
                        checkLiteral();
                    }
                } else {
                    switch ( format.charAt( iFormat ) ) {
                        case "d":
                            day = getNumber( "d" );
                            break;
                        case "D":
                            getName( "D", dayNamesShort, dayNames );
                            break;
                        case "o":
                            doy = getNumber( "o" );
                            break;
                        case "m":
                            month = getNumber( "m" );
                            break;
                        case "M":
                            month = getName( "M", monthNamesShort, monthNames );
                            break;
                        case "y":
                            year = getNumber( "y" );
                            break;
                        case "@":
                            date = new Date( getNumber( "@" ) );
                            year = date.getFullYear();
                            month = date.getMonth() + 1;
                            day = date.getDate();
                            break;
                        case "!":
                            date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
                            year = date.getFullYear();
                            month = date.getMonth() + 1;
                            day = date.getDate();
                            break;
                        case "'":
                            if ( lookAhead( "'" ) ) {
                                checkLiteral();
                            } else {
                                literal = true;
                            }
                            break;
                        default:
                            checkLiteral();
                    }
                }
            }

            if ( iValue < value.length ) {
                extra = value.substr( iValue );
                if ( !/^\s+/.test( extra ) ) {
                    throw "Extra/unparsed characters found in date: " + extra;
                }
            }

            if ( year === -1 ) {
                year = new Date().getFullYear();
            } else if ( year < 100 ) {
                year += new Date().getFullYear() - new Date().getFullYear() % 100 +
                    ( year <= shortYearCutoff ? 0 : -100 );
            }

            if ( doy > -1 ) {
                month = 1;
                day = doy;
                do {
                    dim = this._getDaysInMonth( year, month - 1 );
                    if ( day <= dim ) {
                        break;
                    }
                    month++;
                    day -= dim;
                } while ( true );
            }

            date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
            if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
                throw "Invalid date"; // E.g. 31/02/00
            }
            return date;
        },

        /* Standard date formats. */
        ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
        COOKIE: "D, dd M yy",
        ISO_8601: "yy-mm-dd",
        RFC_822: "D, d M y",
        RFC_850: "DD, dd-M-y",
        RFC_1036: "D, d M y",
        RFC_1123: "D, d M yy",
        RFC_2822: "D, d M yy",
        RSS: "D, d M y", // RFC 822
        TICKS: "!",
        TIMESTAMP: "@",
        W3C: "yy-mm-dd", // ISO 8601

        _ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
            Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),

        /* Format a date object into a string value.
         * The format can be combinations of the following:
         * d  - day of month (no leading zero)
         * dd - day of month (two digit)
         * o  - day of year (no leading zeros)
         * oo - day of year (three digit)
         * D  - day name short
         * DD - day name long
         * m  - month of year (no leading zero)
         * mm - month of year (two digit)
         * M  - month name short
         * MM - month name long
         * y  - year (two digit)
         * yy - year (four digit)
         * @ - Unix timestamp (ms since 01/01/1970)
         * ! - Windows ticks (100ns since 01/01/0001)
         * "..." - literal text
         * '' - single quote
         *
         * @param  format string - the desired format of the date
         * @param  date Date - the date value to format
         * @param  settings Object - attributes include:
         *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
         *					dayNames		string[7] - names of the days from Sunday (optional)
         *					monthNamesShort string[12] - abbreviated names of the months (optional)
         *					monthNames		string[12] - names of the months (optional)
         * @return  string - the date in the above format
         */
        formatDate: function( format, date, settings ) {
            if ( !date ) {
                return "";
            }

            var iFormat,
                dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
                dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
                monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
                monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,

                // Check whether a format character is doubled
                lookAhead = function( match ) {
                    var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
                    if ( matches ) {
                        iFormat++;
                    }
                    return matches;
                },

                // Format a number, with leading zero if necessary
                formatNumber = function( match, value, len ) {
                    var num = "" + value;
                    if ( lookAhead( match ) ) {
                        while ( num.length < len ) {
                            num = "0" + num;
                        }
                    }
                    return num;
                },

                // Format a name, short or long as requested
                formatName = function( match, value, shortNames, longNames ) {
                    return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
                },
                output = "",
                literal = false;

            if ( date ) {
                for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
                    if ( literal ) {
                        if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
                            literal = false;
                        } else {
                            output += format.charAt( iFormat );
                        }
                    } else {
                        switch ( format.charAt( iFormat ) ) {
                            case "d":
                                output += formatNumber( "d", date.getDate(), 2 );
                                break;
                            case "D":
                                output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
                                break;
                            case "o":
                                output += formatNumber( "o",
                                    Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
                                break;
                            case "m":
                                output += formatNumber( "m", date.getMonth() + 1, 2 );
                                break;
                            case "M":
                                output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
                                break;
                            case "y":
                                output += ( lookAhead( "y" ) ? date.getFullYear() :
                                    ( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
                                break;
                            case "@":
                                output += date.getTime();
                                break;
                            case "!":
                                output += date.getTime() * 10000 + this._ticksTo1970;
                                break;
                            case "'":
                                if ( lookAhead( "'" ) ) {
                                    output += "'";
                                } else {
                                    literal = true;
                                }
                                break;
                            default:
                                output += format.charAt( iFormat );
                        }
                    }
                }
            }
            return output;
        },

        /* Extract all possible characters from the date format. */
        _possibleChars: function( format ) {
            var iFormat,
                chars = "",
                literal = false,

                // Check whether a format character is doubled
                lookAhead = function( match ) {
                    var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
                    if ( matches ) {
                        iFormat++;
                    }
                    return matches;
                };

            for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
                if ( literal ) {
                    if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
                        literal = false;
                    } else {
                        chars += format.charAt( iFormat );
                    }
                } else {
                    switch ( format.charAt( iFormat ) ) {
                        case "d": case "m": case "y": case "@":
                            chars += "0123456789";
                            break;
                        case "D": case "M":
                            return null; // Accept anything
                        case "'":
                            if ( lookAhead( "'" ) ) {
                                chars += "'";
                            } else {
                                literal = true;
                            }
                            break;
                        default:
                            chars += format.charAt( iFormat );
                    }
                }
            }
            return chars;
        },

        /* Get a setting value, defaulting if necessary. */
        _get: function( inst, name ) {
            return inst.settings[ name ] !== undefined ?
                inst.settings[ name ] : this._defaults[ name ];
        },

        /* Parse existing date and initialise date picker. */
        _setDateFromField: function( inst, noDefault ) {
            if ( inst.input.val() === inst.lastVal ) {
                return;
            }

            var dateFormat = this._get( inst, "dateFormat" ),
                dates = inst.lastVal = inst.input ? inst.input.val() : null,
                defaultDate = this._getDefaultDate( inst ),
                date = defaultDate,
                settings = this._getFormatConfig( inst );

            try {
                date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
            } catch ( event ) {
                dates = ( noDefault ? "" : dates );
            }
            inst.selectedDay = date.getDate();
            inst.drawMonth = inst.selectedMonth = date.getMonth();
            inst.drawYear = inst.selectedYear = date.getFullYear();
            inst.currentDay = ( dates ? date.getDate() : 0 );
            inst.currentMonth = ( dates ? date.getMonth() : 0 );
            inst.currentYear = ( dates ? date.getFullYear() : 0 );
            this._adjustInstDate( inst );
        },

        /* Retrieve the default date shown on opening. */
        _getDefaultDate: function( inst ) {
            return this._restrictMinMax( inst,
                this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
        },

        /* A date may be specified as an exact value or a relative one. */
        _determineDate: function( inst, date, defaultDate ) {
            var offsetNumeric = function( offset ) {
                    var date = new Date();
                    date.setDate( date.getDate() + offset );
                    return date;
                },
                offsetString = function( offset ) {
                    try {
                        return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
                            offset, $.datepicker._getFormatConfig( inst ) );
                    } catch ( e ) {

                        // Ignore
                    }

                    var date = ( offset.toLowerCase().match( /^c/ ) ?
                            $.datepicker._getDate( inst ) : null ) || new Date(),
                        year = date.getFullYear(),
                        month = date.getMonth(),
                        day = date.getDate(),
                        pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
                        matches = pattern.exec( offset );

                    while ( matches ) {
                        switch ( matches[ 2 ] || "d" ) {
                            case "d" : case "D" :
                                day += parseInt( matches[ 1 ], 10 ); break;
                            case "w" : case "W" :
                                day += parseInt( matches[ 1 ], 10 ) * 7; break;
                            case "m" : case "M" :
                                month += parseInt( matches[ 1 ], 10 );
                                day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
                                break;
                            case "y": case "Y" :
                                year += parseInt( matches[ 1 ], 10 );
                                day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
                                break;
                        }
                        matches = pattern.exec( offset );
                    }
                    return new Date( year, month, day );
                },
                newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
                    ( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );

            newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
            if ( newDate ) {
                newDate.setHours( 0 );
                newDate.setMinutes( 0 );
                newDate.setSeconds( 0 );
                newDate.setMilliseconds( 0 );
            }
            return this._daylightSavingAdjust( newDate );
        },

        /* Handle switch to/from daylight saving.
         * Hours may be non-zero on daylight saving cut-over:
         * > 12 when midnight changeover, but then cannot generate
         * midnight datetime, so jump to 1AM, otherwise reset.
         * @param  date  (Date) the date to check
         * @return  (Date) the corrected date
         */
        _daylightSavingAdjust: function( date ) {
            if ( !date ) {
                return null;
            }
            date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
            return date;
        },

        /* Set the date(s) directly. */
        _setDate: function( inst, date, noChange ) {
            var clear = !date,
                origMonth = inst.selectedMonth,
                origYear = inst.selectedYear,
                newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );

            inst.selectedDay = inst.currentDay = newDate.getDate();
            inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
            inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
            if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
                this._notifyChange( inst );
            }
            this._adjustInstDate( inst );
            if ( inst.input ) {
                inst.input.val( clear ? "" : this._formatDate( inst ) );
            }
        },

        /* Retrieve the date(s) directly. */
        _getDate: function( inst ) {
            var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
                this._daylightSavingAdjust( new Date(
                    inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
            return startDate;
        },

        /* Attach the onxxx handlers.  These are declared statically so
         * they work with static code transformers like Caja.
         */
        _attachHandlers: function( inst ) {
            var stepMonths = this._get( inst, "stepMonths" ),
                id = "#" + inst.id.replace( /\\\\/g, "\\" );
            inst.dpDiv.find( "[data-handler]" ).map( function() {
                var handler = {
                    prev: function() {
                        $.datepicker._adjustDate( id, -stepMonths, "M" );
                    },
                    next: function() {
                        $.datepicker._adjustDate( id, +stepMonths, "M" );
                    },
                    hide: function() {
                        $.datepicker._hideDatepicker();
                    },
                    today: function() {
                        $.datepicker._gotoToday( id );
                    },
                    selectDay: function() {
                        $.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
                        return false;
                    },
                    selectMonth: function() {
                        $.datepicker._selectMonthYear( id, this, "M" );
                        return false;
                    },
                    selectYear: function() {
                        $.datepicker._selectMonthYear( id, this, "Y" );
                        return false;
                    }
                };
                $( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
            } );
        },

        /* Generate the HTML for the current state of the date picker. */
        _generateHTML: function( inst ) {
            var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
                controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
                monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
                selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
                cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
                printDate, dRow, tbody, daySettings, otherMonth, unselectable,
                tempDate = new Date(),
                today = this._daylightSavingAdjust(
                    new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
                isRTL = this._get( inst, "isRTL" ),
                showButtonPanel = this._get( inst, "showButtonPanel" ),
                hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
                navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
                numMonths = this._getNumberOfMonths( inst ),
                showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
                stepMonths = this._get( inst, "stepMonths" ),
                isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
                currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
                    new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
                minDate = this._getMinMaxDate( inst, "min" ),
                maxDate = this._getMinMaxDate( inst, "max" ),
                drawMonth = inst.drawMonth - showCurrentAtPos,
                drawYear = inst.drawYear;

            if ( drawMonth < 0 ) {
                drawMonth += 12;
                drawYear--;
            }
            if ( maxDate ) {
                maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
                    maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
                maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
                while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
                    drawMonth--;
                    if ( drawMonth < 0 ) {
                        drawMonth = 11;
                        drawYear--;
                    }
                }
            }
            inst.drawMonth = drawMonth;
            inst.drawYear = drawYear;

            prevText = this._get( inst, "prevText" );
            prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
                this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
                this._getFormatConfig( inst ) ) );

            if ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ) {
                prev = $( "<a>" )
                    .attr( {
                        "class": "ui-datepicker-prev ui-corner-all",
                        "data-handler": "prev",
                        "data-event": "click",
                        title: prevText
                    } )
                    .append(
                        $( "<span>" )
                            .addClass( "ui-icon ui-icon-circle-triangle-" +
                                ( isRTL ? "e" : "w" ) )
                            .text( prevText )
                    )[ 0 ].outerHTML;
            } else if ( hideIfNoPrevNext ) {
                prev = "";
            } else {
                prev = $( "<a>" )
                    .attr( {
                        "class": "ui-datepicker-prev ui-corner-all ui-state-disabled",
                        title: prevText
                    } )
                    .append(
                        $( "<span>" )
                            .addClass( "ui-icon ui-icon-circle-triangle-" +
                                ( isRTL ? "e" : "w" ) )
                            .text( prevText )
                    )[ 0 ].outerHTML;
            }

            nextText = this._get( inst, "nextText" );
            nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
                this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
                this._getFormatConfig( inst ) ) );

            if ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ) {
                next = $( "<a>" )
                    .attr( {
                        "class": "ui-datepicker-next ui-corner-all",
                        "data-handler": "next",
                        "data-event": "click",
                        title: nextText
                    } )
                    .append(
                        $( "<span>" )
                            .addClass( "ui-icon ui-icon-circle-triangle-" +
                                ( isRTL ? "w" : "e" ) )
                            .text( nextText )
                    )[ 0 ].outerHTML;
            } else if ( hideIfNoPrevNext ) {
                next = "";
            } else {
                next = $( "<a>" )
                    .attr( {
                        "class": "ui-datepicker-next ui-corner-all ui-state-disabled",
                        title: nextText
                    } )
                    .append(
                        $( "<span>" )
                            .attr( "class", "ui-icon ui-icon-circle-triangle-" +
                                ( isRTL ? "w" : "e" ) )
                            .text( nextText )
                    )[ 0 ].outerHTML;
            }

            currentText = this._get( inst, "currentText" );
            gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
            currentText = ( !navigationAsDateFormat ? currentText :
                this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );

            controls = "";
            if ( !inst.inline ) {
                controls = $( "<button>" )
                    .attr( {
                        type: "button",
                        "class": "ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all",
                        "data-handler": "hide",
                        "data-event": "click"
                    } )
                    .text( this._get( inst, "closeText" ) )[ 0 ].outerHTML;
            }

            buttonPanel = "";
            if ( showButtonPanel ) {
                buttonPanel = $( "<div class='ui-datepicker-buttonpane ui-widget-content'>" )
                    .append( isRTL ? controls : "" )
                    .append( this._isInRange( inst, gotoDate ) ?
                        $( "<button>" )
                            .attr( {
                                type: "button",
                                "class": "ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all",
                                "data-handler": "today",
                                "data-event": "click"
                            } )
                            .text( currentText ) :
                        "" )
                    .append( isRTL ? "" : controls )[ 0 ].outerHTML;
            }

            firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
            firstDay = ( isNaN( firstDay ) ? 0 : firstDay );

            showWeek = this._get( inst, "showWeek" );
            dayNames = this._get( inst, "dayNames" );
            dayNamesMin = this._get( inst, "dayNamesMin" );
            monthNames = this._get( inst, "monthNames" );
            monthNamesShort = this._get( inst, "monthNamesShort" );
            beforeShowDay = this._get( inst, "beforeShowDay" );
            showOtherMonths = this._get( inst, "showOtherMonths" );
            selectOtherMonths = this._get( inst, "selectOtherMonths" );
            defaultDate = this._getDefaultDate( inst );
            html = "";

            for ( row = 0; row < numMonths[ 0 ]; row++ ) {
                group = "";
                this.maxRows = 4;
                for ( col = 0; col < numMonths[ 1 ]; col++ ) {
                    selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
                    cornerClass = " ui-corner-all";
                    calender = "";
                    if ( isMultiMonth ) {
                        calender += "<div class='ui-datepicker-group";
                        if ( numMonths[ 1 ] > 1 ) {
                            switch ( col ) {
                                case 0: calender += " ui-datepicker-group-first";
                                    cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
                                case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
                                    cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
                                default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
                            }
                        }
                        calender += "'>";
                    }
                    calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
                        ( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
                        ( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
                        this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
                            row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
                        "</div><table class='ui-datepicker-calendar'><thead>" +
                        "<tr>";
                    thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
                    for ( dow = 0; dow < 7; dow++ ) { // days of the week
                        day = ( dow + firstDay ) % 7;
                        thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
                            "<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
                    }
                    calender += thead + "</tr></thead><tbody>";
                    daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
                    if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
                        inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
                    }
                    leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
                    curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
                    numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
                    this.maxRows = numRows;
                    printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
                    for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
                        calender += "<tr>";
                        tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
                            this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
                        for ( dow = 0; dow < 7; dow++ ) { // create date picker days
                            daySettings = ( beforeShowDay ?
                                beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
                            otherMonth = ( printDate.getMonth() !== drawMonth );
                            unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
                                ( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
                            tbody += "<td class='" +
                                ( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
                                ( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
                                ( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
                                ( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?

                                    // or defaultDate is current printedDate and defaultDate is selectedDate
                                    " " + this._dayOverClass : "" ) + // highlight selected day
                                ( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) +  // highlight unselectable days
                                ( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
                                    ( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
                                    ( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
                                ( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "&#39;" ) + "'" : "" ) + // cell title
                                ( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
                                ( otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
                                    ( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
                                        ( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
                                        ( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
                                        ( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
                                        "' href='#' aria-current='" + ( printDate.getTime() === currentDate.getTime() ? "true" : "false" ) + // mark date as selected for screen reader
                                        "' data-date='" + printDate.getDate() + // store date as data
                                        "'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
                            printDate.setDate( printDate.getDate() + 1 );
                            printDate = this._daylightSavingAdjust( printDate );
                        }
                        calender += tbody + "</tr>";
                    }
                    drawMonth++;
                    if ( drawMonth > 11 ) {
                        drawMonth = 0;
                        drawYear++;
                    }
                    calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
                        ( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
                    group += calender;
                }
                html += group;
            }
            html += buttonPanel;
            inst._keyEvent = false;
            return html;
        },

        /* Generate the month and year header. */
        _generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
                                            secondary, monthNames, monthNamesShort ) {

            var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
                changeMonth = this._get( inst, "changeMonth" ),
                changeYear = this._get( inst, "changeYear" ),
                showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
                selectMonthLabel = this._get( inst, "selectMonthLabel" ),
                selectYearLabel = this._get( inst, "selectYearLabel" ),
                html = "<div class='ui-datepicker-title'>",
                monthHtml = "";

            // Month selection
            if ( secondary || !changeMonth ) {
                monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
            } else {
                inMinYear = ( minDate && minDate.getFullYear() === drawYear );
                inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
                monthHtml += "<select class='ui-datepicker-month' aria-label='" + selectMonthLabel + "' data-handler='selectMonth' data-event='change'>";
                for ( month = 0; month < 12; month++ ) {
                    if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
                        monthHtml += "<option value='" + month + "'" +
                            ( month === drawMonth ? " selected='selected'" : "" ) +
                            ">" + monthNamesShort[ month ] + "</option>";
                    }
                }
                monthHtml += "</select>";
            }

            if ( !showMonthAfterYear ) {
                html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" );
            }

            // Year selection
            if ( !inst.yearshtml ) {
                inst.yearshtml = "";
                if ( secondary || !changeYear ) {
                    html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
                } else {

                    // determine range of years to display
                    years = this._get( inst, "yearRange" ).split( ":" );
                    thisYear = new Date().getFullYear();
                    determineYear = function( value ) {
                        var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
                            ( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
                                parseInt( value, 10 ) ) );
                        return ( isNaN( year ) ? thisYear : year );
                    };
                    year = determineYear( years[ 0 ] );
                    endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
                    year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
                    endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
                    inst.yearshtml += "<select class='ui-datepicker-year' aria-label='" + selectYearLabel + "' data-handler='selectYear' data-event='change'>";
                    for ( ; year <= endYear; year++ ) {
                        inst.yearshtml += "<option value='" + year + "'" +
                            ( year === drawYear ? " selected='selected'" : "" ) +
                            ">" + year + "</option>";
                    }
                    inst.yearshtml += "</select>";

                    html += inst.yearshtml;
                    inst.yearshtml = null;
                }
            }

            html += this._get( inst, "yearSuffix" );
            if ( showMonthAfterYear ) {
                html += ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" ) + monthHtml;
            }
            html += "</div>"; // Close datepicker_header
            return html;
        },

        /* Adjust one of the date sub-fields. */
        _adjustInstDate: function( inst, offset, period ) {
            var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
                month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
                day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
                date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );

            inst.selectedDay = date.getDate();
            inst.drawMonth = inst.selectedMonth = date.getMonth();
            inst.drawYear = inst.selectedYear = date.getFullYear();
            if ( period === "M" || period === "Y" ) {
                this._notifyChange( inst );
            }
        },

        /* Ensure a date is within any min/max bounds. */
        _restrictMinMax: function( inst, date ) {
            var minDate = this._getMinMaxDate( inst, "min" ),
                maxDate = this._getMinMaxDate( inst, "max" ),
                newDate = ( minDate && date < minDate ? minDate : date );
            return ( maxDate && newDate > maxDate ? maxDate : newDate );
        },

        /* Notify change of month/year. */
        _notifyChange: function( inst ) {
            var onChange = this._get( inst, "onChangeMonthYear" );
            if ( onChange ) {
                onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
                    [ inst.selectedYear, inst.selectedMonth + 1, inst ] );
            }
        },

        /* Determine the number of months to show. */
        _getNumberOfMonths: function( inst ) {
            var numMonths = this._get( inst, "numberOfMonths" );
            return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
        },

        /* Determine the current maximum date - ensure no time components are set. */
        _getMinMaxDate: function( inst, minMax ) {
            return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
        },

        /* Find the number of days in a given month. */
        _getDaysInMonth: function( year, month ) {
            return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
        },

        /* Find the day of the week of the first of a month. */
        _getFirstDayOfMonth: function( year, month ) {
            return new Date( year, month, 1 ).getDay();
        },

        /* Determines if we should allow a "next/prev" month display change. */
        _canAdjustMonth: function( inst, offset, curYear, curMonth ) {
            var numMonths = this._getNumberOfMonths( inst ),
                date = this._daylightSavingAdjust( new Date( curYear,
                    curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );

            if ( offset < 0 ) {
                date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
            }
            return this._isInRange( inst, date );
        },

        /* Is the given date in the accepted range? */
        _isInRange: function( inst, date ) {
            var yearSplit, currentYear,
                minDate = this._getMinMaxDate( inst, "min" ),
                maxDate = this._getMinMaxDate( inst, "max" ),
                minYear = null,
                maxYear = null,
                years = this._get( inst, "yearRange" );
            if ( years ) {
                yearSplit = years.split( ":" );
                currentYear = new Date().getFullYear();
                minYear = parseInt( yearSplit[ 0 ], 10 );
                maxYear = parseInt( yearSplit[ 1 ], 10 );
                if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
                    minYear += currentYear;
                }
                if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
                    maxYear += currentYear;
                }
            }

            return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
                ( !maxDate || date.getTime() <= maxDate.getTime() ) &&
                ( !minYear || date.getFullYear() >= minYear ) &&
                ( !maxYear || date.getFullYear() <= maxYear ) );
        },

        /* Provide the configuration settings for formatting/parsing. */
        _getFormatConfig: function( inst ) {
            var shortYearCutoff = this._get( inst, "shortYearCutoff" );
            shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
                new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
            return { shortYearCutoff: shortYearCutoff,
                dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
                monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
        },

        /* Format the given date for display. */
        _formatDate: function( inst, day, month, year ) {
            if ( !day ) {
                inst.currentDay = inst.selectedDay;
                inst.currentMonth = inst.selectedMonth;
                inst.currentYear = inst.selectedYear;
            }
            var date = ( day ? ( typeof day === "object" ? day :
                    this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
                this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
            return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
        }
    } );

    /*
     * Bind hover events for datepicker elements.
     * Done via delegate so the binding only occurs once in the lifetime of the parent div.
     * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
     */
    function datepicker_bindHover( dpDiv ) {
        var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
        return dpDiv.on( "mouseout", selector, function() {
            $( this ).removeClass( "ui-state-hover" );
            if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
                $( this ).removeClass( "ui-datepicker-prev-hover" );
            }
            if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
                $( this ).removeClass( "ui-datepicker-next-hover" );
            }
        } )
            .on( "mouseover", selector, datepicker_handleMouseover );
    }

    function datepicker_handleMouseover() {
        if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
            $( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
            $( this ).addClass( "ui-state-hover" );
            if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
                $( this ).addClass( "ui-datepicker-prev-hover" );
            }
            if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
                $( this ).addClass( "ui-datepicker-next-hover" );
            }
        }
    }

    /* jQuery extend now ignores nulls! */
    function datepicker_extendRemove( target, props ) {
        $.extend( target, props );
        for ( var name in props ) {
            if ( props[ name ] == null ) {
                target[ name ] = props[ name ];
            }
        }
        return target;
    }

    /* Invoke the datepicker functionality.
       @param  options  string - a command, optionally followed by additional parameters or
                        Object - settings for attaching new datepicker functionality
       @return  jQuery object */
    $.fn.datepicker = function( options ) {

        /* Verify an empty collection wasn't passed - Fixes #6976 */
        if ( !this.length ) {
            return this;
        }

        /* Initialise the date picker. */
        if ( !$.datepicker.initialized ) {
            $( document ).on( "mousedown", $.datepicker._checkExternalClick );
            $.datepicker.initialized = true;
        }

        /* Append datepicker main container to body if not exist. */
        if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
            $( "body" ).append( $.datepicker.dpDiv );
        }

        var otherArgs = Array.prototype.slice.call( arguments, 1 );
        if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
            return $.datepicker[ "_" + options + "Datepicker" ].
            apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
        }
        if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
            return $.datepicker[ "_" + options + "Datepicker" ].
            apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
        }
        return this.each( function() {
            if ( typeof options === "string" ) {
                $.datepicker[ "_" + options + "Datepicker" ]
                    .apply( $.datepicker, [ this ].concat( otherArgs ) );
            } else {
                $.datepicker._attachDatepicker( this, options );
            }
        } );
    };

    $.datepicker = new Datepicker(); // singleton instance
    $.datepicker.initialized = false;
    $.datepicker.uuid = new Date().getTime();
    $.datepicker.version = "1.13.2";

    return $.datepicker;

} );

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

//>>label: Menu
//>>group: Widgets
//>>description: Creates nestable menus.
//>>docs: http://api.jqueryui.com/menu/
//>>demos: http://jqueryui.com/menu/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/menu.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/menu',[
            "jquery",
            "../keycode",
            "../position",
            "../safe-active-element",
            "../unique-id",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    return $.widget( "ui.menu", {
        version: "1.13.2",
        defaultElement: "<ul>",
        delay: 300,
        options: {
            icons: {
                submenu: "ui-icon-caret-1-e"
            },
            items: "> *",
            menus: "ul",
            position: {
                my: "left top",
                at: "right top"
            },
            role: "menu",

            // Callbacks
            blur: null,
            focus: null,
            select: null
        },

        _create: function() {
            this.activeMenu = this.element;

            // Flag used to prevent firing of the click handler
            // as the event bubbles up through nested menus
            this.mouseHandled = false;
            this.lastMousePosition = { x: null, y: null };
            this.element
                .uniqueId()
                .attr( {
                    role: this.options.role,
                    tabIndex: 0
                } );

            this._addClass( "ui-menu", "ui-widget ui-widget-content" );
            this._on( {

                // Prevent focus from sticking to links inside menu after clicking
                // them (focus should always stay on UL during navigation).
                "mousedown .ui-menu-item": function( event ) {
                    event.preventDefault();

                    this._activateItem( event );
                },
                "click .ui-menu-item": function( event ) {
                    var target = $( event.target );
                    var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
                    if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
                        this.select( event );

                        // Only set the mouseHandled flag if the event will bubble, see #9469.
                        if ( !event.isPropagationStopped() ) {
                            this.mouseHandled = true;
                        }

                        // Open submenu on click
                        if ( target.has( ".ui-menu" ).length ) {
                            this.expand( event );
                        } else if ( !this.element.is( ":focus" ) &&
                            active.closest( ".ui-menu" ).length ) {

                            // Redirect focus to the menu
                            this.element.trigger( "focus", [ true ] );

                            // If the active item is on the top level, let it stay active.
                            // Otherwise, blur the active item since it is no longer visible.
                            if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
                                clearTimeout( this.timer );
                            }
                        }
                    }
                },
                "mouseenter .ui-menu-item": "_activateItem",
                "mousemove .ui-menu-item": "_activateItem",
                mouseleave: "collapseAll",
                "mouseleave .ui-menu": "collapseAll",
                focus: function( event, keepActiveItem ) {

                    // If there's already an active item, keep it active
                    // If not, activate the first item
                    var item = this.active || this._menuItems().first();

                    if ( !keepActiveItem ) {
                        this.focus( event, item );
                    }
                },
                blur: function( event ) {
                    this._delay( function() {
                        var notContained = !$.contains(
                            this.element[ 0 ],
                            $.ui.safeActiveElement( this.document[ 0 ] )
                        );
                        if ( notContained ) {
                            this.collapseAll( event );
                        }
                    } );
                },
                keydown: "_keydown"
            } );

            this.refresh();

            // Clicks outside of a menu collapse any open menus
            this._on( this.document, {
                click: function( event ) {
                    if ( this._closeOnDocumentClick( event ) ) {
                        this.collapseAll( event, true );
                    }

                    // Reset the mouseHandled flag
                    this.mouseHandled = false;
                }
            } );
        },

        _activateItem: function( event ) {

            // Ignore mouse events while typeahead is active, see #10458.
            // Prevents focusing the wrong item when typeahead causes a scroll while the mouse
            // is over an item in the menu
            if ( this.previousFilter ) {
                return;
            }

            // If the mouse didn't actually move, but the page was scrolled, ignore the event (#9356)
            if ( event.clientX === this.lastMousePosition.x &&
                event.clientY === this.lastMousePosition.y ) {
                return;
            }

            this.lastMousePosition = {
                x: event.clientX,
                y: event.clientY
            };

            var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
                target = $( event.currentTarget );

            // Ignore bubbled events on parent items, see #11641
            if ( actualTarget[ 0 ] !== target[ 0 ] ) {
                return;
            }

            // If the item is already active, there's nothing to do
            if ( target.is( ".ui-state-active" ) ) {
                return;
            }

            // Remove ui-state-active class from siblings of the newly focused menu item
            // to avoid a jump caused by adjacent elements both having a class with a border
            this._removeClass( target.siblings().children( ".ui-state-active" ),
                null, "ui-state-active" );
            this.focus( event, target );
        },

        _destroy: function() {
            var items = this.element.find( ".ui-menu-item" )
                    .removeAttr( "role aria-disabled" ),
                submenus = items.children( ".ui-menu-item-wrapper" )
                    .removeUniqueId()
                    .removeAttr( "tabIndex role aria-haspopup" );

            // Destroy (sub)menus
            this.element
                .removeAttr( "aria-activedescendant" )
                .find( ".ui-menu" ).addBack()
                .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
                    "tabIndex" )
                .removeUniqueId()
                .show();

            submenus.children().each( function() {
                var elem = $( this );
                if ( elem.data( "ui-menu-submenu-caret" ) ) {
                    elem.remove();
                }
            } );
        },

        _keydown: function( event ) {
            var match, prev, character, skip,
                preventDefault = true;

            switch ( event.keyCode ) {
                case $.ui.keyCode.PAGE_UP:
                    this.previousPage( event );
                    break;
                case $.ui.keyCode.PAGE_DOWN:
                    this.nextPage( event );
                    break;
                case $.ui.keyCode.HOME:
                    this._move( "first", "first", event );
                    break;
                case $.ui.keyCode.END:
                    this._move( "last", "last", event );
                    break;
                case $.ui.keyCode.UP:
                    this.previous( event );
                    break;
                case $.ui.keyCode.DOWN:
                    this.next( event );
                    break;
                case $.ui.keyCode.LEFT:
                    this.collapse( event );
                    break;
                case $.ui.keyCode.RIGHT:
                    if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
                        this.expand( event );
                    }
                    break;
                case $.ui.keyCode.ENTER:
                case $.ui.keyCode.SPACE:
                    this._activate( event );
                    break;
                case $.ui.keyCode.ESCAPE:
                    this.collapse( event );
                    break;
                default:
                    preventDefault = false;
                    prev = this.previousFilter || "";
                    skip = false;

                    // Support number pad values
                    character = event.keyCode >= 96 && event.keyCode <= 105 ?
                        ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );

                    clearTimeout( this.filterTimer );

                    if ( character === prev ) {
                        skip = true;
                    } else {
                        character = prev + character;
                    }

                    match = this._filterMenuItems( character );
                    match = skip && match.index( this.active.next() ) !== -1 ?
                        this.active.nextAll( ".ui-menu-item" ) :
                        match;

                    // If no matches on the current filter, reset to the last character pressed
                    // to move down the menu to the first item that starts with that character
                    if ( !match.length ) {
                        character = String.fromCharCode( event.keyCode );
                        match = this._filterMenuItems( character );
                    }

                    if ( match.length ) {
                        this.focus( event, match );
                        this.previousFilter = character;
                        this.filterTimer = this._delay( function() {
                            delete this.previousFilter;
                        }, 1000 );
                    } else {
                        delete this.previousFilter;
                    }
            }

            if ( preventDefault ) {
                event.preventDefault();
            }
        },

        _activate: function( event ) {
            if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
                if ( this.active.children( "[aria-haspopup='true']" ).length ) {
                    this.expand( event );
                } else {
                    this.select( event );
                }
            }
        },

        refresh: function() {
            var menus, items, newSubmenus, newItems, newWrappers,
                that = this,
                icon = this.options.icons.submenu,
                submenus = this.element.find( this.options.menus );

            this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );

            // Initialize nested menus
            newSubmenus = submenus.filter( ":not(.ui-menu)" )
                .hide()
                .attr( {
                    role: this.options.role,
                    "aria-hidden": "true",
                    "aria-expanded": "false"
                } )
                .each( function() {
                    var menu = $( this ),
                        item = menu.prev(),
                        submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );

                    that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
                    item
                        .attr( "aria-haspopup", "true" )
                        .prepend( submenuCaret );
                    menu.attr( "aria-labelledby", item.attr( "id" ) );
                } );

            this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );

            menus = submenus.add( this.element );
            items = menus.find( this.options.items );

            // Initialize menu-items containing spaces and/or dashes only as dividers
            items.not( ".ui-menu-item" ).each( function() {
                var item = $( this );
                if ( that._isDivider( item ) ) {
                    that._addClass( item, "ui-menu-divider", "ui-widget-content" );
                }
            } );

            // Don't refresh list items that are already adapted
            newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
            newWrappers = newItems.children()
                .not( ".ui-menu" )
                .uniqueId()
                .attr( {
                    tabIndex: -1,
                    role: this._itemRole()
                } );
            this._addClass( newItems, "ui-menu-item" )
                ._addClass( newWrappers, "ui-menu-item-wrapper" );

            // Add aria-disabled attribute to any disabled menu item
            items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );

            // If the active item has been removed, blur the menu
            if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
                this.blur();
            }
        },

        _itemRole: function() {
            return {
                menu: "menuitem",
                listbox: "option"
            }[ this.options.role ];
        },

        _setOption: function( key, value ) {
            if ( key === "icons" ) {
                var icons = this.element.find( ".ui-menu-icon" );
                this._removeClass( icons, null, this.options.icons.submenu )
                    ._addClass( icons, null, value.submenu );
            }
            this._super( key, value );
        },

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

            this.element.attr( "aria-disabled", String( value ) );
            this._toggleClass( null, "ui-state-disabled", !!value );
        },

        focus: function( event, item ) {
            var nested, focused, activeParent;
            this.blur( event, event && event.type === "focus" );

            this._scrollIntoView( item );

            this.active = item.first();

            focused = this.active.children( ".ui-menu-item-wrapper" );
            this._addClass( focused, null, "ui-state-active" );

            // Only update aria-activedescendant if there's a role
            // otherwise we assume focus is managed elsewhere
            if ( this.options.role ) {
                this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
            }

            // Highlight active parent menu item, if any
            activeParent = this.active
                .parent()
                .closest( ".ui-menu-item" )
                .children( ".ui-menu-item-wrapper" );
            this._addClass( activeParent, null, "ui-state-active" );

            if ( event && event.type === "keydown" ) {
                this._close();
            } else {
                this.timer = this._delay( function() {
                    this._close();
                }, this.delay );
            }

            nested = item.children( ".ui-menu" );
            if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
                this._startOpening( nested );
            }
            this.activeMenu = item.parent();

            this._trigger( "focus", event, { item: item } );
        },

        _scrollIntoView: function( item ) {
            var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
            if ( this._hasScroll() ) {
                borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
                paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
                offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
                scroll = this.activeMenu.scrollTop();
                elementHeight = this.activeMenu.height();
                itemHeight = item.outerHeight();

                if ( offset < 0 ) {
                    this.activeMenu.scrollTop( scroll + offset );
                } else if ( offset + itemHeight > elementHeight ) {
                    this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
                }
            }
        },

        blur: function( event, fromFocus ) {
            if ( !fromFocus ) {
                clearTimeout( this.timer );
            }

            if ( !this.active ) {
                return;
            }

            this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
                null, "ui-state-active" );

            this._trigger( "blur", event, { item: this.active } );
            this.active = null;
        },

        _startOpening: function( submenu ) {
            clearTimeout( this.timer );

            // Don't open if already open fixes a Firefox bug that caused a .5 pixel
            // shift in the submenu position when mousing over the caret icon
            if ( submenu.attr( "aria-hidden" ) !== "true" ) {
                return;
            }

            this.timer = this._delay( function() {
                this._close();
                this._open( submenu );
            }, this.delay );
        },

        _open: function( submenu ) {
            var position = $.extend( {
                of: this.active
            }, this.options.position );

            clearTimeout( this.timer );
            this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
                .hide()
                .attr( "aria-hidden", "true" );

            submenu
                .show()
                .removeAttr( "aria-hidden" )
                .attr( "aria-expanded", "true" )
                .position( position );
        },

        collapseAll: function( event, all ) {
            clearTimeout( this.timer );
            this.timer = this._delay( function() {

                // If we were passed an event, look for the submenu that contains the event
                var currentMenu = all ? this.element :
                    $( event && event.target ).closest( this.element.find( ".ui-menu" ) );

                // If we found no valid submenu ancestor, use the main menu to close all
                // sub menus anyway
                if ( !currentMenu.length ) {
                    currentMenu = this.element;
                }

                this._close( currentMenu );

                this.blur( event );

                // Work around active item staying active after menu is blurred
                this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );

                this.activeMenu = currentMenu;
            }, all ? 0 : this.delay );
        },

        // With no arguments, closes the currently active menu - if nothing is active
        // it closes all menus.  If passed an argument, it will search for menus BELOW
        _close: function( startMenu ) {
            if ( !startMenu ) {
                startMenu = this.active ? this.active.parent() : this.element;
            }

            startMenu.find( ".ui-menu" )
                .hide()
                .attr( "aria-hidden", "true" )
                .attr( "aria-expanded", "false" );
        },

        _closeOnDocumentClick: function( event ) {
            return !$( event.target ).closest( ".ui-menu" ).length;
        },

        _isDivider: function( item ) {

            // Match hyphen, em dash, en dash
            return !/[^\-\u2014\u2013\s]/.test( item.text() );
        },

        collapse: function( event ) {
            var newItem = this.active &&
                this.active.parent().closest( ".ui-menu-item", this.element );
            if ( newItem && newItem.length ) {
                this._close();
                this.focus( event, newItem );
            }
        },

        expand: function( event ) {
            var newItem = this.active && this._menuItems( this.active.children( ".ui-menu" ) ).first();

            if ( newItem && newItem.length ) {
                this._open( newItem.parent() );

                // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
                this._delay( function() {
                    this.focus( event, newItem );
                } );
            }
        },

        next: function( event ) {
            this._move( "next", "first", event );
        },

        previous: function( event ) {
            this._move( "prev", "last", event );
        },

        isFirstItem: function() {
            return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
        },

        isLastItem: function() {
            return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
        },

        _menuItems: function( menu ) {
            return ( menu || this.element )
                .find( this.options.items )
                .filter( ".ui-menu-item" );
        },

        _move: function( direction, filter, event ) {
            var next;
            if ( this.active ) {
                if ( direction === "first" || direction === "last" ) {
                    next = this.active
                        [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
                        .last();
                } else {
                    next = this.active
                        [ direction + "All" ]( ".ui-menu-item" )
                        .first();
                }
            }
            if ( !next || !next.length || !this.active ) {
                next = this._menuItems( this.activeMenu )[ filter ]();
            }

            this.focus( event, next );
        },

        nextPage: function( event ) {
            var item, base, height;

            if ( !this.active ) {
                this.next( event );
                return;
            }
            if ( this.isLastItem() ) {
                return;
            }
            if ( this._hasScroll() ) {
                base = this.active.offset().top;
                height = this.element.innerHeight();

                // jQuery 3.2 doesn't include scrollbars in innerHeight, add it back.
                if ( $.fn.jquery.indexOf( "3.2." ) === 0 ) {
                    height += this.element[ 0 ].offsetHeight - this.element.outerHeight();
                }

                this.active.nextAll( ".ui-menu-item" ).each( function() {
                    item = $( this );
                    return item.offset().top - base - height < 0;
                } );

                this.focus( event, item );
            } else {
                this.focus( event, this._menuItems( this.activeMenu )
                    [ !this.active ? "first" : "last" ]() );
            }
        },

        previousPage: function( event ) {
            var item, base, height;
            if ( !this.active ) {
                this.next( event );
                return;
            }
            if ( this.isFirstItem() ) {
                return;
            }
            if ( this._hasScroll() ) {
                base = this.active.offset().top;
                height = this.element.innerHeight();

                // jQuery 3.2 doesn't include scrollbars in innerHeight, add it back.
                if ( $.fn.jquery.indexOf( "3.2." ) === 0 ) {
                    height += this.element[ 0 ].offsetHeight - this.element.outerHeight();
                }

                this.active.prevAll( ".ui-menu-item" ).each( function() {
                    item = $( this );
                    return item.offset().top - base + height > 0;
                } );

                this.focus( event, item );
            } else {
                this.focus( event, this._menuItems( this.activeMenu ).first() );
            }
        },

        _hasScroll: function() {
            return this.element.outerHeight() < this.element.prop( "scrollHeight" );
        },

        select: function( event ) {

            // TODO: It should never be possible to not have an active item at this
            // point, but the tests don't trigger mouseenter before click.
            this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
            var ui = { item: this.active };
            if ( !this.active.has( ".ui-menu" ).length ) {
                this.collapseAll( event, true );
            }
            this._trigger( "select", event, ui );
        },

        _filterMenuItems: function( character ) {
            var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
                regex = new RegExp( "^" + escapedCharacter, "i" );

            return this.activeMenu
                .find( this.options.items )

                // Only match on items, not dividers or other content (#10571)
                .filter( ".ui-menu-item" )
                .filter( function() {
                    return regex.test(
                        String.prototype.trim.call(
                            $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
                } );
        }
    } );

} );

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

//>>label: Selectmenu
//>>group: Widgets
/* eslint-disable max-len */
//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
/* eslint-enable max-len */
//>>docs: http://api.jqueryui.com/selectmenu/
//>>demos: http://jqueryui.com/selectmenu/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/selectmenu',[
            "jquery",
            "./menu",
            "../form-reset-mixin",
            "../keycode",
            "../labels",
            "../position",
            "../unique-id",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    return $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
        version: "1.13.2",
        defaultElement: "<select>",
        options: {
            appendTo: null,
            classes: {
                "ui-selectmenu-button-open": "ui-corner-top",
                "ui-selectmenu-button-closed": "ui-corner-all"
            },
            disabled: null,
            icons: {
                button: "ui-icon-triangle-1-s"
            },
            position: {
                my: "left top",
                at: "left bottom",
                collision: "none"
            },
            width: false,

            // Callbacks
            change: null,
            close: null,
            focus: null,
            open: null,
            select: null
        },

        _create: function() {
            var selectmenuId = this.element.uniqueId().attr( "id" );
            this.ids = {
                element: selectmenuId,
                button: selectmenuId + "-button",
                menu: selectmenuId + "-menu"
            };

            this._drawButton();
            this._drawMenu();
            this._bindFormResetHandler();

            this._rendered = false;
            this.menuItems = $();
        },

        _drawButton: function() {
            var icon,
                that = this,
                item = this._parseOption(
                    this.element.find( "option:selected" ),
                    this.element[ 0 ].selectedIndex
                );

            // Associate existing label with the new button
            this.labels = this.element.labels().attr( "for", this.ids.button );
            this._on( this.labels, {
                click: function( event ) {
                    this.button.trigger( "focus" );
                    event.preventDefault();
                }
            } );

            // Hide original select element
            this.element.hide();

            // Create button
            this.button = $( "<span>", {
                tabindex: this.options.disabled ? -1 : 0,
                id: this.ids.button,
                role: "combobox",
                "aria-expanded": "false",
                "aria-autocomplete": "list",
                "aria-owns": this.ids.menu,
                "aria-haspopup": "true",
                title: this.element.attr( "title" )
            } )
                .insertAfter( this.element );

            this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed",
                "ui-button ui-widget" );

            icon = $( "<span>" ).appendTo( this.button );
            this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button );
            this.buttonItem = this._renderButtonItem( item )
                .appendTo( this.button );

            if ( this.options.width !== false ) {
                this._resizeButton();
            }

            this._on( this.button, this._buttonEvents );
            this.button.one( "focusin", function() {

                // Delay rendering the menu items until the button receives focus.
                // The menu may have already been rendered via a programmatic open.
                if ( !that._rendered ) {
                    that._refreshMenu();
                }
            } );
        },

        _drawMenu: function() {
            var that = this;

            // Create menu
            this.menu = $( "<ul>", {
                "aria-hidden": "true",
                "aria-labelledby": this.ids.button,
                id: this.ids.menu
            } );

            // Wrap menu
            this.menuWrap = $( "<div>" ).append( this.menu );
            this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" );
            this.menuWrap.appendTo( this._appendTo() );

            // Initialize menu widget
            this.menuInstance = this.menu
                .menu( {
                    classes: {
                        "ui-menu": "ui-corner-bottom"
                    },
                    role: "listbox",
                    select: function( event, ui ) {
                        event.preventDefault();

                        // Support: IE8
                        // If the item was selected via a click, the text selection
                        // will be destroyed in IE
                        that._setSelection();

                        that._select( ui.item.data( "ui-selectmenu-item" ), event );
                    },
                    focus: function( event, ui ) {
                        var item = ui.item.data( "ui-selectmenu-item" );

                        // Prevent inital focus from firing and check if its a newly focused item
                        if ( that.focusIndex != null && item.index !== that.focusIndex ) {
                            that._trigger( "focus", event, { item: item } );
                            if ( !that.isOpen ) {
                                that._select( item, event );
                            }
                        }
                        that.focusIndex = item.index;

                        that.button.attr( "aria-activedescendant",
                            that.menuItems.eq( item.index ).attr( "id" ) );
                    }
                } )
                .menu( "instance" );

            // Don't close the menu on mouseleave
            this.menuInstance._off( this.menu, "mouseleave" );

            // Cancel the menu's collapseAll on document click
            this.menuInstance._closeOnDocumentClick = function() {
                return false;
            };

            // Selects often contain empty items, but never contain dividers
            this.menuInstance._isDivider = function() {
                return false;
            };
        },

        refresh: function() {
            this._refreshMenu();
            this.buttonItem.replaceWith(
                this.buttonItem = this._renderButtonItem(

                    // Fall back to an empty object in case there are no options
                    this._getSelectedItem().data( "ui-selectmenu-item" ) || {}
                )
            );
            if ( this.options.width === null ) {
                this._resizeButton();
            }
        },

        _refreshMenu: function() {
            var item,
                options = this.element.find( "option" );

            this.menu.empty();

            this._parseOptions( options );
            this._renderMenu( this.menu, this.items );

            this.menuInstance.refresh();
            this.menuItems = this.menu.find( "li" )
                .not( ".ui-selectmenu-optgroup" )
                .find( ".ui-menu-item-wrapper" );

            this._rendered = true;

            if ( !options.length ) {
                return;
            }

            item = this._getSelectedItem();

            // Update the menu to have the correct item focused
            this.menuInstance.focus( null, item );
            this._setAria( item.data( "ui-selectmenu-item" ) );

            // Set disabled state
            this._setOption( "disabled", this.element.prop( "disabled" ) );
        },

        open: function( event ) {
            if ( this.options.disabled ) {
                return;
            }

            // If this is the first time the menu is being opened, render the items
            if ( !this._rendered ) {
                this._refreshMenu();
            } else {

                // Menu clears focus on close, reset focus to selected item
                this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" );
                this.menuInstance.focus( null, this._getSelectedItem() );
            }

            // If there are no options, don't open the menu
            if ( !this.menuItems.length ) {
                return;
            }

            this.isOpen = true;
            this._toggleAttr();
            this._resizeMenu();
            this._position();

            this._on( this.document, this._documentClick );

            this._trigger( "open", event );
        },

        _position: function() {
            this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
        },

        close: function( event ) {
            if ( !this.isOpen ) {
                return;
            }

            this.isOpen = false;
            this._toggleAttr();

            this.range = null;
            this._off( this.document );

            this._trigger( "close", event );
        },

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

        menuWidget: function() {
            return this.menu;
        },

        _renderButtonItem: function( item ) {
            var buttonItem = $( "<span>" );

            this._setText( buttonItem, item.label );
            this._addClass( buttonItem, "ui-selectmenu-text" );

            return buttonItem;
        },

        _renderMenu: function( ul, items ) {
            var that = this,
                currentOptgroup = "";

            $.each( items, function( index, item ) {
                var li;

                if ( item.optgroup !== currentOptgroup ) {
                    li = $( "<li>", {
                        text: item.optgroup
                    } );
                    that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" +
                        ( item.element.parent( "optgroup" ).prop( "disabled" ) ?
                            " ui-state-disabled" :
                            "" ) );

                    li.appendTo( ul );

                    currentOptgroup = item.optgroup;
                }

                that._renderItemData( ul, item );
            } );
        },

        _renderItemData: function( ul, item ) {
            return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
        },

        _renderItem: function( ul, item ) {
            var li = $( "<li>" ),
                wrapper = $( "<div>", {
                    title: item.element.attr( "title" )
                } );

            if ( item.disabled ) {
                this._addClass( li, null, "ui-state-disabled" );
            }
            this._setText( wrapper, item.label );

            return li.append( wrapper ).appendTo( ul );
        },

        _setText: function( element, value ) {
            if ( value ) {
                element.text( value );
            } else {
                element.html( "&#160;" );
            }
        },

        _move: function( direction, event ) {
            var item, next,
                filter = ".ui-menu-item";

            if ( this.isOpen ) {
                item = this.menuItems.eq( this.focusIndex ).parent( "li" );
            } else {
                item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
                filter += ":not(.ui-state-disabled)";
            }

            if ( direction === "first" || direction === "last" ) {
                next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
            } else {
                next = item[ direction + "All" ]( filter ).eq( 0 );
            }

            if ( next.length ) {
                this.menuInstance.focus( event, next );
            }
        },

        _getSelectedItem: function() {
            return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
        },

        _toggle: function( event ) {
            this[ this.isOpen ? "close" : "open" ]( event );
        },

        _setSelection: function() {
            var selection;

            if ( !this.range ) {
                return;
            }

            if ( window.getSelection ) {
                selection = window.getSelection();
                selection.removeAllRanges();
                selection.addRange( this.range );

                // Support: IE8
            } else {
                this.range.select();
            }

            // Support: IE
            // Setting the text selection kills the button focus in IE, but
            // restoring the focus doesn't kill the selection.
            this.button.trigger( "focus" );
        },

        _documentClick: {
            mousedown: function( event ) {
                if ( !this.isOpen ) {
                    return;
                }

                if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
                    $.escapeSelector( this.ids.button ) ).length ) {
                    this.close( event );
                }
            }
        },

        _buttonEvents: {

            // Prevent text selection from being reset when interacting with the selectmenu (#10144)
            mousedown: function() {
                var selection;

                if ( window.getSelection ) {
                    selection = window.getSelection();
                    if ( selection.rangeCount ) {
                        this.range = selection.getRangeAt( 0 );
                    }

                    // Support: IE8
                } else {
                    this.range = document.selection.createRange();
                }
            },

            click: function( event ) {
                this._setSelection();
                this._toggle( event );
            },

            keydown: function( event ) {
                var preventDefault = true;
                switch ( event.keyCode ) {
                    case $.ui.keyCode.TAB:
                    case $.ui.keyCode.ESCAPE:
                        this.close( event );
                        preventDefault = false;
                        break;
                    case $.ui.keyCode.ENTER:
                        if ( this.isOpen ) {
                            this._selectFocusedItem( event );
                        }
                        break;
                    case $.ui.keyCode.UP:
                        if ( event.altKey ) {
                            this._toggle( event );
                        } else {
                            this._move( "prev", event );
                        }
                        break;
                    case $.ui.keyCode.DOWN:
                        if ( event.altKey ) {
                            this._toggle( event );
                        } else {
                            this._move( "next", event );
                        }
                        break;
                    case $.ui.keyCode.SPACE:
                        if ( this.isOpen ) {
                            this._selectFocusedItem( event );
                        } else {
                            this._toggle( event );
                        }
                        break;
                    case $.ui.keyCode.LEFT:
                        this._move( "prev", event );
                        break;
                    case $.ui.keyCode.RIGHT:
                        this._move( "next", event );
                        break;
                    case $.ui.keyCode.HOME:
                    case $.ui.keyCode.PAGE_UP:
                        this._move( "first", event );
                        break;
                    case $.ui.keyCode.END:
                    case $.ui.keyCode.PAGE_DOWN:
                        this._move( "last", event );
                        break;
                    default:
                        this.menu.trigger( event );
                        preventDefault = false;
                }

                if ( preventDefault ) {
                    event.preventDefault();
                }
            }
        },

        _selectFocusedItem: function( event ) {
            var item = this.menuItems.eq( this.focusIndex ).parent( "li" );
            if ( !item.hasClass( "ui-state-disabled" ) ) {
                this._select( item.data( "ui-selectmenu-item" ), event );
            }
        },

        _select: function( item, event ) {
            var oldIndex = this.element[ 0 ].selectedIndex;

            // Change native select element
            this.element[ 0 ].selectedIndex = item.index;
            this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) );
            this._setAria( item );
            this._trigger( "select", event, { item: item } );

            if ( item.index !== oldIndex ) {
                this._trigger( "change", event, { item: item } );
            }

            this.close( event );
        },

        _setAria: function( item ) {
            var id = this.menuItems.eq( item.index ).attr( "id" );

            this.button.attr( {
                "aria-labelledby": id,
                "aria-activedescendant": id
            } );
            this.menu.attr( "aria-activedescendant", id );
        },

        _setOption: function( key, value ) {
            if ( key === "icons" ) {
                var icon = this.button.find( "span.ui-icon" );
                this._removeClass( icon, null, this.options.icons.button )
                    ._addClass( icon, null, value.button );
            }

            this._super( key, value );

            if ( key === "appendTo" ) {
                this.menuWrap.appendTo( this._appendTo() );
            }

            if ( key === "width" ) {
                this._resizeButton();
            }
        },

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

            this.menuInstance.option( "disabled", value );
            this.button.attr( "aria-disabled", value );
            this._toggleClass( this.button, null, "ui-state-disabled", value );

            this.element.prop( "disabled", value );
            if ( value ) {
                this.button.attr( "tabindex", -1 );
                this.close();
            } else {
                this.button.attr( "tabindex", 0 );
            }
        },

        _appendTo: function() {
            var element = this.options.appendTo;

            if ( element ) {
                element = element.jquery || element.nodeType ?
                    $( element ) :
                    this.document.find( element ).eq( 0 );
            }

            if ( !element || !element[ 0 ] ) {
                element = this.element.closest( ".ui-front, dialog" );
            }

            if ( !element.length ) {
                element = this.document[ 0 ].body;
            }

            return element;
        },

        _toggleAttr: function() {
            this.button.attr( "aria-expanded", this.isOpen );

            // We can't use two _toggleClass() calls here, because we need to make sure
            // we always remove classes first and add them second, otherwise if both classes have the
            // same theme class, it will be removed after we add it.
            this._removeClass( this.button, "ui-selectmenu-button-" +
                ( this.isOpen ? "closed" : "open" ) )
                ._addClass( this.button, "ui-selectmenu-button-" +
                    ( this.isOpen ? "open" : "closed" ) )
                ._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen );

            this.menu.attr( "aria-hidden", !this.isOpen );
        },

        _resizeButton: function() {
            var width = this.options.width;

            // For `width: false`, just remove inline style and stop
            if ( width === false ) {
                this.button.css( "width", "" );
                return;
            }

            // For `width: null`, match the width of the original element
            if ( width === null ) {
                width = this.element.show().outerWidth();
                this.element.hide();
            }

            this.button.outerWidth( width );
        },

        _resizeMenu: function() {
            this.menu.outerWidth( Math.max(
                this.button.outerWidth(),

                // Support: IE10
                // IE10 wraps long text (possibly a rounding bug)
                // so we add 1px to avoid the wrapping
                this.menu.width( "" ).outerWidth() + 1
            ) );
        },

        _getCreateOptions: function() {
            var options = this._super();

            options.disabled = this.element.prop( "disabled" );

            return options;
        },

        _parseOptions: function( options ) {
            var that = this,
                data = [];
            options.each( function( index, item ) {
                if ( item.hidden ) {
                    return;
                }

                data.push( that._parseOption( $( item ), index ) );
            } );
            this.items = data;
        },

        _parseOption: function( option, index ) {
            var optgroup = option.parent( "optgroup" );

            return {
                element: option,
                index: index,
                value: option.val(),
                label: option.text(),
                optgroup: optgroup.attr( "label" ) || "",
                disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
            };
        },

        _destroy: function() {
            this._unbindFormResetHandler();
            this.menuWrap.remove();
            this.button.remove();
            this.element.show();
            this.element.removeUniqueId();
            this.labels.attr( "for", this.ids.element );
        }
    } ] );

} );

/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20
* http://trentrichardson.com/examples/timepicker
* Copyright (c) 2016 Trent Richardson; Licensed MIT */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define('jquery/timepicker',['jquery', 'jquery-ui-modules/datepicker', 'jquery-ui-modules/slider'], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {

    /*
    * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
    */
    $.ui.timepicker = $.ui.timepicker || {};
    if ($.ui.timepicker.version) {
        return;
    }

    /*
    * Extend jQueryUI, get it started with our version number
    */
    $.extend($.ui, {
        timepicker: {
            version: "1.6.3"
        }
    });

    /*
    * Timepicker manager.
    * Use the singleton instance of this class, $.timepicker, to interact with the time picker.
    * Settings for (groups of) time pickers are maintained in an instance object,
    * allowing multiple different settings on the same page.
    */
    var Timepicker = function () {
        this.regional = []; // Available regional settings, indexed by language code
        this.regional[''] = { // Default regional settings
            currentText: 'Now',
            closeText: 'Done',
            amNames: ['AM', 'A'],
            pmNames: ['PM', 'P'],
            timeFormat: 'HH:mm',
            timeSuffix: '',
            timeOnlyTitle: 'Choose Time',
            timeText: 'Time',
            hourText: 'Hour',
            minuteText: 'Minute',
            secondText: 'Second',
            millisecText: 'Millisecond',
            microsecText: 'Microsecond',
            timezoneText: 'Time Zone',
            isRTL: false
        };
        this._defaults = { // Global defaults for all the datetime picker instances
            showButtonPanel: true,
            timeOnly: false,
            timeOnlyShowDate: false,
            showHour: null,
            showMinute: null,
            showSecond: null,
            showMillisec: null,
            showMicrosec: null,
            showTimezone: null,
            showTime: true,
            stepHour: 1,
            stepMinute: 1,
            stepSecond: 1,
            stepMillisec: 1,
            stepMicrosec: 1,
            hour: 0,
            minute: 0,
            second: 0,
            millisec: 0,
            microsec: 0,
            timezone: null,
            hourMin: 0,
            minuteMin: 0,
            secondMin: 0,
            millisecMin: 0,
            microsecMin: 0,
            hourMax: 23,
            minuteMax: 59,
            secondMax: 59,
            millisecMax: 999,
            microsecMax: 999,
            minDateTime: null,
            maxDateTime: null,
            maxTime: null,
            minTime: null,
            onSelect: null,
            hourGrid: 0,
            minuteGrid: 0,
            secondGrid: 0,
            millisecGrid: 0,
            microsecGrid: 0,
            alwaysSetTime: true,
            separator: ' ',
            altFieldTimeOnly: true,
            altTimeFormat: null,
            altSeparator: null,
            altTimeSuffix: null,
            altRedirectFocus: true,
            pickerTimeFormat: null,
            pickerTimeSuffix: null,
            showTimepicker: true,
            timezoneList: null,
            addSliderAccess: false,
            sliderAccessArgs: null,
            controlType: 'slider',
            oneLine: false,
            defaultValue: null,
            parse: 'strict',
            afterInject: null
        };
        $.extend(this._defaults, this.regional['']);
    };

    $.extend(Timepicker.prototype, {
        $input: null,
        $altInput: null,
        $timeObj: null,
        inst: null,
        hour_slider: null,
        minute_slider: null,
        second_slider: null,
        millisec_slider: null,
        microsec_slider: null,
        timezone_select: null,
        maxTime: null,
        minTime: null,
        hour: 0,
        minute: 0,
        second: 0,
        millisec: 0,
        microsec: 0,
        timezone: null,
        hourMinOriginal: null,
        minuteMinOriginal: null,
        secondMinOriginal: null,
        millisecMinOriginal: null,
        microsecMinOriginal: null,
        hourMaxOriginal: null,
        minuteMaxOriginal: null,
        secondMaxOriginal: null,
        millisecMaxOriginal: null,
        microsecMaxOriginal: null,
        ampm: '',
        formattedDate: '',
        formattedTime: '',
        formattedDateTime: '',
        timezoneList: null,
        units: ['hour', 'minute', 'second', 'millisec', 'microsec'],
        support: {},
        control: null,

        /*
        * Override the default settings for all instances of the time picker.
        * @param  {Object} settings  object - the new settings to use as defaults (anonymous object)
        * @return {Object} the manager object
        */
        setDefaults: function (settings) {
            extendRemove(this._defaults, settings || {});
            return this;
        },

        /*
        * Create a new Timepicker instance
        */
        _newInst: function ($input, opts) {
            var tp_inst = new Timepicker(),
                inlineSettings = {},
                fns = {},
                overrides, i;

            for (var attrName in this._defaults) {
                if (this._defaults.hasOwnProperty(attrName)) {
                    var attrValue = $input.attr('time:' + attrName);
                    if (attrValue) {
                        try {
                            inlineSettings[attrName] = eval(attrValue);
                        } catch (err) {
                            inlineSettings[attrName] = attrValue;
                        }
                    }
                }
            }

            overrides = {
                beforeShow: function (input, dp_inst) {
                    if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {
                        return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);
                    }
                },
                onChangeMonthYear: function (year, month, dp_inst) {
                    // Update the time as well : this prevents the time from disappearing from the $input field.
                    // tp_inst._updateDateTime(dp_inst);
                    if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {
                        tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
                    }
                },
                onClose: function (dateText, dp_inst) {
                    if (tp_inst.timeDefined === true && $input.val() !== '') {
                        tp_inst._updateDateTime(dp_inst);
                    }
                    if ($.isFunction(tp_inst._defaults.evnts.onClose)) {
                        tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);
                    }
                }
            };
            for (i in overrides) {
                if (overrides.hasOwnProperty(i)) {
                    fns[i] = opts[i] || this._defaults[i] || null;
                }
            }

            tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {
                evnts: fns,
                timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
            });
            tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {
                return val.toUpperCase();
            });
            tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {
                return val.toUpperCase();
            });

            // detect which units are supported
            tp_inst.support = detectSupport(
                tp_inst._defaults.timeFormat +
                (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +
                (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));

            // controlType is string - key to our this._controls
            if (typeof(tp_inst._defaults.controlType) === 'string') {
                if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {
                    tp_inst._defaults.controlType = 'select';
                }
                tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];
            }
            // controlType is an object and must implement create, options, value methods
            else {
                tp_inst.control = tp_inst._defaults.controlType;
            }

            // prep the timezone options
            var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,
                0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];
            if (tp_inst._defaults.timezoneList !== null) {
                timezoneList = tp_inst._defaults.timezoneList;
            }
            var tzl = timezoneList.length, tzi = 0, tzv = null;
            if (tzl > 0 && typeof timezoneList[0] !== 'object') {
                for (; tzi < tzl; tzi++) {
                    tzv = timezoneList[tzi];
                    timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };
                }
            }
            tp_inst._defaults.timezoneList = timezoneList;

            // set the default units
            tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :
                ((new Date()).getTimezoneOffset() * -1);
            tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :
                tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;
            tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :
                tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;
            tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :
                tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;
            tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :
                tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;
            tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :
                tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;
            tp_inst.ampm = '';
            tp_inst.$input = $input;

            if (tp_inst._defaults.altField) {
                tp_inst.$altInput = $(tp_inst._defaults.altField);
                if (tp_inst._defaults.altRedirectFocus === true) {
                    tp_inst.$altInput.css({
                        cursor: 'pointer'
                    }).focus(function () {
                        $input.trigger("focus");
                    });
                }
            }

            if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {
                tp_inst._defaults.minDate = new Date();
            }
            if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {
                tp_inst._defaults.maxDate = new Date();
            }

            // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
            if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {
                tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
            }
            if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {
                tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
            }
            if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {
                tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
            }
            if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {
                tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
            }
            tp_inst.$input.bind('focus', function () {
                tp_inst._onFocus();
            });

            return tp_inst;
        },

        /*
        * add our sliders to the calendar
        */
        _addTimePicker: function (dp_inst) {
            var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val());

            this.timeDefined = this._parseTime(currDT);
            this._limitMinMaxDateTime(dp_inst, false);
            this._injectTimePicker();
            this._afterInject();
        },

        /*
        * parse the time string from input value or _setTime
        */
        _parseTime: function (timeString, withDate) {
            if (!this.inst) {
                this.inst = $.datepicker._getInst(this.$input[0]);
            }

            if (withDate || !this._defaults.timeOnly) {
                var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
                try {
                    var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);
                    if (!parseRes.timeObj) {
                        return false;
                    }
                    $.extend(this, parseRes.timeObj);
                } catch (err) {
                    $.timepicker.log("Error parsing the date/time string: " + err +
                        "\ndate/time string = " + timeString +
                        "\ntimeFormat = " + this._defaults.timeFormat +
                        "\ndateFormat = " + dp_dateFormat);
                    return false;
                }
                return true;
            } else {
                var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);
                if (!timeObj) {
                    return false;
                }
                $.extend(this, timeObj);
                return true;
            }
        },

        /*
        * Handle callback option after injecting timepicker
        */
        _afterInject: function() {
            var o = this.inst.settings;
            if ($.isFunction(o.afterInject)) {
                o.afterInject.call(this);
            }
        },

        /*
        * generate and inject html for timepicker into ui datepicker
        */
        _injectTimePicker: function () {
            var $dp = this.inst.dpDiv,
                o = this.inst.settings,
                tp_inst = this,
                litem = '',
                uitem = '',
                show = null,
                max = {},
                gridSize = {},
                size = null,
                i = 0,
                l = 0;

            // Prevent displaying twice
            if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {
                var noDisplay = ' ui_tpicker_unit_hide',
                    html = '<div class="ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + (o.oneLine && o.controlType === 'select' ? ' ui-timepicker-oneLine' : '') + '"><dl>' + '<dt class="ui_tpicker_time_label' + ((o.showTime) ? '' : noDisplay) + '">' + o.timeText + '</dt>' +
                        '<dd class="ui_tpicker_time '+ ((o.showTime) ? '' : noDisplay) + '"><input class="ui_tpicker_time_input" ' + (o.timeInput ? '' : 'disabled') + '/></dd>';

                // Create the markup
                for (i = 0, l = this.units.length; i < l; i++) {
                    litem = this.units[i];
                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];

                    // Added by Peter Medeiros:
                    // - Figure out what the hour/minute/second max should be based on the step values.
                    // - Example: if stepMinute is 15, then minMax is 45.
                    max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);
                    gridSize[litem] = 0;

                    html += '<dt class="ui_tpicker_' + litem + '_label' + (show ? '' : noDisplay) + '">' + o[litem + 'Text'] + '</dt>' +
                        '<dd class="ui_tpicker_' + litem + (show ? '' : noDisplay) + '"><div class="ui_tpicker_' + litem + '_slider' + (show ? '' : noDisplay) + '"></div>';

                    if (show && o[litem + 'Grid'] > 0) {
                        html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';

                        if (litem === 'hour') {
                            for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {
                                gridSize[litem]++;
                                var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);
                                html += '<td data-for="' + litem + '">' + tmph + '</td>';
                            }
                        }
                        else {
                            for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {
                                gridSize[litem]++;
                                html += '<td data-for="' + litem + '">' + ((m < 10) ? '0' : '') + m + '</td>';
                            }
                        }

                        html += '</tr></table></div>';
                    }
                    html += '</dd>';
                }

                // Timezone
                var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;
                html += '<dt class="ui_tpicker_timezone_label' + (showTz ? '' : noDisplay) + '">' + o.timezoneText + '</dt>';
                html += '<dd class="ui_tpicker_timezone' + (showTz ? '' : noDisplay) + '"></dd>';

                // Create the elements from string
                html += '</dl></div>';
                var $tp = $(html);

                // if we only want time picker...
                if (o.timeOnly === true) {
                    $tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');
                    $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
                }

                // add sliders, adjust grids, add events
                for (i = 0, l = tp_inst.units.length; i < l; i++) {
                    litem = tp_inst.units[i];
                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];

                    // add the slider
                    tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);

                    // adjust the grid and add click event
                    if (show && o[litem + 'Grid'] > 0) {
                        size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);
                        $tp.find('.ui_tpicker_' + litem + ' table').css({
                            width: size + "%",
                            marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"),
                            marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0',
                            borderCollapse: 'collapse'
                        }).find("td").click(function (e) {
                            var $t = $(this),
                                h = $t.html(),
                                n = parseInt(h.replace(/[^0-9]/g), 10),
                                ap = h.replace(/[^apm]/ig),
                                f = $t.data('for'); // loses scope, so we use data-for

                            if (f === 'hour') {
                                if (ap.indexOf('p') !== -1 && n < 12) {
                                    n += 12;
                                }
                                else {
                                    if (ap.indexOf('a') !== -1 && n === 12) {
                                        n = 0;
                                    }
                                }
                            }

                            tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);

                            tp_inst._onTimeChange();
                            tp_inst._onSelectHandler();
                        }).css({
                            cursor: 'pointer',
                            width: (100 / gridSize[litem]) + '%',
                            textAlign: 'center',
                            overflow: 'hidden'
                        });
                    } // end if grid > 0
                } // end for loop

                // Add timezone options
                this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");
                $.fn.append.apply(this.timezone_select,
                    $.map(o.timezoneList, function (val, idx) {
                        return $("<option />").val(typeof val === "object" ? val.value : val).text(typeof val === "object" ? val.label : val);
                    }));
                if (typeof(this.timezone) !== "undefined" && this.timezone !== null && this.timezone !== "") {
                    var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;
                    if (local_timezone === this.timezone) {
                        selectLocalTimezone(tp_inst);
                    } else {
                        this.timezone_select.val(this.timezone);
                    }
                } else {
                    if (typeof(this.hour) !== "undefined" && this.hour !== null && this.hour !== "") {
                        this.timezone_select.val(o.timezone);
                    } else {
                        selectLocalTimezone(tp_inst);
                    }
                }
                this.timezone_select.change(function () {
                    tp_inst._onTimeChange();
                    tp_inst._onSelectHandler();
                    tp_inst._afterInject();
                });
                // End timezone options

                // inject timepicker into datepicker
                var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
                if ($buttonPanel.length) {
                    $buttonPanel.before($tp);
                } else {
                    $dp.append($tp);
                }

                this.$timeObj = $tp.find('.ui_tpicker_time_input');
                this.$timeObj.change(function () {
                    var timeFormat = tp_inst.inst.settings.timeFormat;
                    var parsedTime = $.datepicker.parseTime(timeFormat, this.value);
                    var update = new Date();
                    if (parsedTime) {
                        update.setHours(parsedTime.hour);
                        update.setMinutes(parsedTime.minute);
                        update.setSeconds(parsedTime.second);
                        $.datepicker._setTime(tp_inst.inst, update);
                    } else {
                        this.value = tp_inst.formattedTime;
                        this.blur();
                    }
                });

                if (this.inst !== null) {
                    var timeDefined = this.timeDefined;
                    this._onTimeChange();
                    this.timeDefined = timeDefined;
                }

                // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
                if (this._defaults.addSliderAccess) {
                    var sliderAccessArgs = this._defaults.sliderAccessArgs,
                        rtl = this._defaults.isRTL;
                    sliderAccessArgs.isRTL = rtl;

                    setTimeout(function () { // fix for inline mode
                        if ($tp.find('.ui-slider-access').length === 0) {
                            $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);

                            // fix any grids since sliders are shorter
                            var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
                            if (sliderAccessWidth) {
                                $tp.find('table:visible').each(function () {
                                    var $g = $(this),
                                        oldWidth = $g.outerWidth(),
                                        oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),
                                        newWidth = oldWidth - sliderAccessWidth,
                                        newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',
                                        css = { width: newWidth, marginRight: 0, marginLeft: 0 };
                                    css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;
                                    $g.css(css);
                                });
                            }
                        }
                    }, 10);
                }
                // end slideAccess integration

                tp_inst._limitMinMaxDateTime(this.inst, true);
            }
        },

        /*
        * This function tries to limit the ability to go outside the
        * min/max date range
        */
        _limitMinMaxDateTime: function (dp_inst, adjustSliders) {
            var o = this._defaults,
                dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);

            if (!this._defaults.showTimepicker) {
                return;
            } // No time so nothing to check here

            if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {
                var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
                    minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);

                if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {
                    this.hourMinOriginal = o.hourMin;
                    this.minuteMinOriginal = o.minuteMin;
                    this.secondMinOriginal = o.secondMin;
                    this.millisecMinOriginal = o.millisecMin;
                    this.microsecMinOriginal = o.microsecMin;
                }

                if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {
                    this._defaults.hourMin = minDateTime.getHours();
                    if (this.hour <= this._defaults.hourMin) {
                        this.hour = this._defaults.hourMin;
                        this._defaults.minuteMin = minDateTime.getMinutes();
                        if (this.minute <= this._defaults.minuteMin) {
                            this.minute = this._defaults.minuteMin;
                            this._defaults.secondMin = minDateTime.getSeconds();
                            if (this.second <= this._defaults.secondMin) {
                                this.second = this._defaults.secondMin;
                                this._defaults.millisecMin = minDateTime.getMilliseconds();
                                if (this.millisec <= this._defaults.millisecMin) {
                                    this.millisec = this._defaults.millisecMin;
                                    this._defaults.microsecMin = minDateTime.getMicroseconds();
                                } else {
                                    if (this.microsec < this._defaults.microsecMin) {
                                        this.microsec = this._defaults.microsecMin;
                                    }
                                    this._defaults.microsecMin = this.microsecMinOriginal;
                                }
                            } else {
                                this._defaults.millisecMin = this.millisecMinOriginal;
                                this._defaults.microsecMin = this.microsecMinOriginal;
                            }
                        } else {
                            this._defaults.secondMin = this.secondMinOriginal;
                            this._defaults.millisecMin = this.millisecMinOriginal;
                            this._defaults.microsecMin = this.microsecMinOriginal;
                        }
                    } else {
                        this._defaults.minuteMin = this.minuteMinOriginal;
                        this._defaults.secondMin = this.secondMinOriginal;
                        this._defaults.millisecMin = this.millisecMinOriginal;
                        this._defaults.microsecMin = this.microsecMinOriginal;
                    }
                } else {
                    this._defaults.hourMin = this.hourMinOriginal;
                    this._defaults.minuteMin = this.minuteMinOriginal;
                    this._defaults.secondMin = this.secondMinOriginal;
                    this._defaults.millisecMin = this.millisecMinOriginal;
                    this._defaults.microsecMin = this.microsecMinOriginal;
                }
            }

            if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {
                var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
                    maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);

                if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {
                    this.hourMaxOriginal = o.hourMax;
                    this.minuteMaxOriginal = o.minuteMax;
                    this.secondMaxOriginal = o.secondMax;
                    this.millisecMaxOriginal = o.millisecMax;
                    this.microsecMaxOriginal = o.microsecMax;
                }

                if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {
                    this._defaults.hourMax = maxDateTime.getHours();
                    if (this.hour >= this._defaults.hourMax) {
                        this.hour = this._defaults.hourMax;
                        this._defaults.minuteMax = maxDateTime.getMinutes();
                        if (this.minute >= this._defaults.minuteMax) {
                            this.minute = this._defaults.minuteMax;
                            this._defaults.secondMax = maxDateTime.getSeconds();
                            if (this.second >= this._defaults.secondMax) {
                                this.second = this._defaults.secondMax;
                                this._defaults.millisecMax = maxDateTime.getMilliseconds();
                                if (this.millisec >= this._defaults.millisecMax) {
                                    this.millisec = this._defaults.millisecMax;
                                    this._defaults.microsecMax = maxDateTime.getMicroseconds();
                                } else {
                                    if (this.microsec > this._defaults.microsecMax) {
                                        this.microsec = this._defaults.microsecMax;
                                    }
                                    this._defaults.microsecMax = this.microsecMaxOriginal;
                                }
                            } else {
                                this._defaults.millisecMax = this.millisecMaxOriginal;
                                this._defaults.microsecMax = this.microsecMaxOriginal;
                            }
                        } else {
                            this._defaults.secondMax = this.secondMaxOriginal;
                            this._defaults.millisecMax = this.millisecMaxOriginal;
                            this._defaults.microsecMax = this.microsecMaxOriginal;
                        }
                    } else {
                        this._defaults.minuteMax = this.minuteMaxOriginal;
                        this._defaults.secondMax = this.secondMaxOriginal;
                        this._defaults.millisecMax = this.millisecMaxOriginal;
                        this._defaults.microsecMax = this.microsecMaxOriginal;
                    }
                } else {
                    this._defaults.hourMax = this.hourMaxOriginal;
                    this._defaults.minuteMax = this.minuteMaxOriginal;
                    this._defaults.secondMax = this.secondMaxOriginal;
                    this._defaults.millisecMax = this.millisecMaxOriginal;
                    this._defaults.microsecMax = this.microsecMaxOriginal;
                }
            }

            if (dp_inst.settings.minTime!==null) {
                var tempMinTime=new Date("01/01/1970 " + dp_inst.settings.minTime);
                if (this.hour<tempMinTime.getHours()) {
                    this.hour=this._defaults.hourMin=tempMinTime.getHours();
                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();
                } else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {
                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();
                } else {
                    if (this._defaults.hourMin<tempMinTime.getHours()) {
                        this._defaults.hourMin=tempMinTime.getHours();
                        this._defaults.minuteMin=tempMinTime.getMinutes();
                    } else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {
                        this._defaults.minuteMin=tempMinTime.getMinutes();
                    } else {
                        this._defaults.minuteMin=0;
                    }
                }
            }

            if (dp_inst.settings.maxTime!==null) {
                var tempMaxTime=new Date("01/01/1970 " + dp_inst.settings.maxTime);
                if (this.hour>tempMaxTime.getHours()) {
                    this.hour=this._defaults.hourMax=tempMaxTime.getHours();
                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();
                } else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {
                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();
                } else {
                    if (this._defaults.hourMax>tempMaxTime.getHours()) {
                        this._defaults.hourMax=tempMaxTime.getHours();
                        this._defaults.minuteMax=tempMaxTime.getMinutes();
                    } else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {
                        this._defaults.minuteMax=tempMaxTime.getMinutes();
                    } else {
                        this._defaults.minuteMax=59;
                    }
                }
            }

            if (adjustSliders !== undefined && adjustSliders === true) {
                var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),
                    minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),
                    secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),
                    millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),
                    microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);

                if (this.hour_slider) {
                    this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });
                    this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));
                }
                if (this.minute_slider) {
                    this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });
                    this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));
                }
                if (this.second_slider) {
                    this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });
                    this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));
                }
                if (this.millisec_slider) {
                    this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });
                    this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));
                }
                if (this.microsec_slider) {
                    this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });
                    this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));
                }
            }

        },

        /*
        * when a slider moves, set the internal time...
        * on time change is also called when the time is updated in the text field
        */
        _onTimeChange: function () {
            if (!this._defaults.showTimepicker) {
                return;
            }
            var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,
                minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,
                second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,
                millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,
                microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,
                timezone = (this.timezone_select) ? this.timezone_select.val() : false,
                o = this._defaults,
                pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
                pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;

            if (typeof(hour) === 'object') {
                hour = false;
            }
            if (typeof(minute) === 'object') {
                minute = false;
            }
            if (typeof(second) === 'object') {
                second = false;
            }
            if (typeof(millisec) === 'object') {
                millisec = false;
            }
            if (typeof(microsec) === 'object') {
                microsec = false;
            }
            if (typeof(timezone) === 'object') {
                timezone = false;
            }

            if (hour !== false) {
                hour = parseInt(hour, 10);
            }
            if (minute !== false) {
                minute = parseInt(minute, 10);
            }
            if (second !== false) {
                second = parseInt(second, 10);
            }
            if (millisec !== false) {
                millisec = parseInt(millisec, 10);
            }
            if (microsec !== false) {
                microsec = parseInt(microsec, 10);
            }
            if (timezone !== false) {
                timezone = timezone.toString();
            }

            var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];

            // If the update was done in the input field, the input field should not be updated.
            // If the update was done using the sliders, update the input field.
            var hasChanged = (
                hour !== parseInt(this.hour,10) || // sliders should all be numeric
                minute !== parseInt(this.minute,10) ||
                second !== parseInt(this.second,10) ||
                millisec !== parseInt(this.millisec,10) ||
                microsec !== parseInt(this.microsec,10) ||
                (this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) ||
                (this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or "EST" format, so use toString()
            );

            if (hasChanged) {

                if (hour !== false) {
                    this.hour = hour;
                }
                if (minute !== false) {
                    this.minute = minute;
                }
                if (second !== false) {
                    this.second = second;
                }
                if (millisec !== false) {
                    this.millisec = millisec;
                }
                if (microsec !== false) {
                    this.microsec = microsec;
                }
                if (timezone !== false) {
                    this.timezone = timezone;
                }

                if (!this.inst) {
                    this.inst = $.datepicker._getInst(this.$input[0]);
                }

                this._limitMinMaxDateTime(this.inst, true);
            }
            if (this.support.ampm) {
                this.ampm = ampm;
            }

            // Updates the time within the timepicker
            this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);
            if (this.$timeObj) {
                if (pickerTimeFormat === o.timeFormat) {
                    this.$timeObj.val(this.formattedTime + pickerTimeSuffix);
                }
                else {
                    this.$timeObj.val($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);
                }
                if (this.$timeObj[0].setSelectionRange) {
                    var sPos = this.$timeObj[0].selectionStart;
                    var ePos = this.$timeObj[0].selectionEnd;
                    this.$timeObj[0].setSelectionRange(sPos, ePos);
                }
            }

            this.timeDefined = true;
            if (hasChanged) {
                this._updateDateTime();
                //this.$input.focus(); // may automatically open the picker on setDate
            }
        },

        /*
        * call custom onSelect.
        * bind to sliders slidestop, and grid click.
        */
        _onSelectHandler: function () {
            var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
            var inputEl = this.$input ? this.$input[0] : null;
            if (onSelect && inputEl) {
                onSelect.apply(inputEl, [this.formattedDateTime, this]);
            }
        },

        /*
        * update our input with the new date time..
        */
        _updateDateTime: function (dp_inst) {
            dp_inst = this.inst || dp_inst;
            var dtTmp = (dp_inst.currentYear > 0?
                new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :
                new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
                dt = $.datepicker._daylightSavingAdjust(dtTmp),
                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),
                dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
                formatCfg = $.datepicker._getFormatConfig(dp_inst),
                timeAvailable = dt !== null && this.timeDefined;
            this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
            var formattedDateTime = this.formattedDate;

            // if a slider was changed but datepicker doesn't have a value yet, set it
            if (dp_inst.lastVal === "") {
                dp_inst.currentYear = dp_inst.selectedYear;
                dp_inst.currentMonth = dp_inst.selectedMonth;
                dp_inst.currentDay = dp_inst.selectedDay;
            }

            /*
            * remove following lines to force every changes in date picker to change the input value
            * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.
            * If the user manually empty the value in the input field, the date picker will never change selected value.
            */
            //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
            //	return;
            //}

            if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {
                formattedDateTime = this.formattedTime;
            } else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {
                formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
            }

            this.formattedDateTime = formattedDateTime;

            if (!this._defaults.showTimepicker) {
                this.$input.val(this.formattedDate);
            } else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {
                this.$altInput.val(this.formattedTime);
                this.$input.val(this.formattedDate);
            } else if (this.$altInput) {
                this.$input.val(formattedDateTime);
                var altFormattedDateTime = '',
                    altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,
                    altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;

                if (!this._defaults.timeOnly) {
                    if (this._defaults.altFormat) {
                        altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);
                    }
                    else {
                        altFormattedDateTime = this.formattedDate;
                    }

                    if (altFormattedDateTime) {
                        altFormattedDateTime += altSeparator;
                    }
                }

                if (this._defaults.altTimeFormat !== null) {
                    altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;
                }
                else {
                    altFormattedDateTime += this.formattedTime + altTimeSuffix;
                }
                this.$altInput.val(altFormattedDateTime);
            } else {
                this.$input.val(formattedDateTime);
            }

            this.$input.trigger("change");
        },

        _onFocus: function () {
            if (!this.$input.val() && this._defaults.defaultValue) {
                this.$input.val(this._defaults.defaultValue);
                var inst = $.datepicker._getInst(this.$input.get(0)),
                    tp_inst = $.datepicker._get(inst, 'timepicker');
                if (tp_inst) {
                    if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
                        try {
                            $.datepicker._updateDatepicker(inst);
                        } catch (err) {
                            $.timepicker.log(err);
                        }
                    }
                }
            }
        },

        /*
        * Small abstraction to control types
        * We can add more, just be sure to follow the pattern: create, options, value
        */
        _controls: {
            // slider methods
            slider: {
                create: function (tp_inst, obj, unit, val, min, max, step) {
                    var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
                    return obj.prop('slide', null).slider({
                        orientation: "horizontal",
                        value: rtl ? val * -1 : val,
                        min: rtl ? max * -1 : min,
                        max: rtl ? min * -1 : max,
                        step: step,
                        slide: function (event, ui) {
                            tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);
                            tp_inst._onTimeChange();
                        },
                        stop: function (event, ui) {
                            tp_inst._onSelectHandler();
                        }
                    });
                },
                options: function (tp_inst, obj, unit, opts, val) {
                    if (tp_inst._defaults.isRTL) {
                        if (typeof(opts) === 'string') {
                            if (opts === 'min' || opts === 'max') {
                                if (val !== undefined) {
                                    return obj.slider(opts, val * -1);
                                }
                                return Math.abs(obj.slider(opts));
                            }
                            return obj.slider(opts);
                        }
                        var min = opts.min,
                            max = opts.max;
                        opts.min = opts.max = null;
                        if (min !== undefined) {
                            opts.max = min * -1;
                        }
                        if (max !== undefined) {
                            opts.min = max * -1;
                        }
                        return obj.slider(opts);
                    }
                    if (typeof(opts) === 'string' && val !== undefined) {
                        return obj.slider(opts, val);
                    }
                    return obj.slider(opts);
                },
                value: function (tp_inst, obj, unit, val) {
                    if (tp_inst._defaults.isRTL) {
                        if (val !== undefined) {
                            return obj.slider('value', val * -1);
                        }
                        return Math.abs(obj.slider('value'));
                    }
                    if (val !== undefined) {
                        return obj.slider('value', val);
                    }
                    return obj.slider('value');
                }
            },
            // select methods
            select: {
                create: function (tp_inst, obj, unit, val, min, max, step) {
                    var sel = '<select class="ui-timepicker-select ui-state-default ui-corner-all" data-unit="' + unit + '" data-min="' + min + '" data-max="' + max + '" data-step="' + step + '">',
                        format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;

                    for (var i = min; i <= max; i += step) {
                        sel += '<option value="' + i + '"' + (i === val ? ' selected' : '') + '>';
                        if (unit === 'hour') {
                            sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);
                        }
                        else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }
                        else {sel += '0' + i.toString(); }
                        sel += '</option>';
                    }
                    sel += '</select>';

                    obj.children('select').remove();

                    $(sel).appendTo(obj).change(function (e) {
                        tp_inst._onTimeChange();
                        tp_inst._onSelectHandler();
                        tp_inst._afterInject();
                    });

                    return obj;
                },
                options: function (tp_inst, obj, unit, opts, val) {
                    var o = {},
                        $t = obj.children('select');
                    if (typeof(opts) === 'string') {
                        if (val === undefined) {
                            return $t.data(opts);
                        }
                        o[opts] = val;
                    }
                    else { o = opts; }
                    return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min>=0 ? o.min : $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));
                },
                value: function (tp_inst, obj, unit, val) {
                    var $t = obj.children('select');
                    if (val !== undefined) {
                        return $t.val(val);
                    }
                    return $t.val();
                }
            }
        } // end _controls

    });

    $.fn.extend({
        /*
        * shorthand just to use timepicker.
        */
        timepicker: function (o) {
            o = o || {};
            var tmp_args = Array.prototype.slice.call(arguments);

            if (typeof o === 'object') {
                tmp_args[0] = $.extend(o, {
                    timeOnly: true
                });
            }

            return $(this).each(function () {
                $.fn.datetimepicker.apply($(this), tmp_args);
            });
        },

        /*
        * extend timepicker to datepicker
        */
        datetimepicker: function (o) {
            o = o || {};
            var tmp_args = arguments;

            if (typeof(o) === 'string') {
                if (o === 'getDate'  || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {
                    return $.fn.datepicker.apply($(this[0]), tmp_args);
                } else {
                    return this.each(function () {
                        var $t = $(this);
                        $t.datepicker.apply($t, tmp_args);
                    });
                }
            } else {
                return this.each(function () {
                    var $t = $(this);
                    $t.datepicker($.timepicker._newInst($t, o)._defaults);
                });
            }
        }
    });

    /*
    * Public Utility to parse date and time
    */
    $.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
        var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);
        if (parseRes.timeObj) {
            var t = parseRes.timeObj;
            parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);
            parseRes.date.setMicroseconds(t.microsec);
        }

        return parseRes.date;
    };

    /*
    * Public utility to parse time
    */
    $.datepicker.parseTime = function (timeFormat, timeString, options) {
        var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),
            iso8601 = (timeFormat.replace(/\'.*?\'/g, '').indexOf('Z') !== -1);

        // Strict parse requires the timeString to match the timeFormat exactly
        var strictParse = function (f, s, o) {

            // pattern for standard and localized AM/PM markers
            var getPatternAmpm = function (amNames, pmNames) {
                var markers = [];
                if (amNames) {
                    $.merge(markers, amNames);
                }
                if (pmNames) {
                    $.merge(markers, pmNames);
                }
                markers = $.map(markers, function (val) {
                    return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');
                });
                return '(' + markers.join('|') + ')?';
            };

            // figure out position of time elements.. cause js cant do named captures
            var getFormatPositions = function (timeFormat) {
                var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),
                    orders = {
                        h: -1,
                        m: -1,
                        s: -1,
                        l: -1,
                        c: -1,
                        t: -1,
                        z: -1
                    };

                if (finds) {
                    for (var i = 0; i < finds.length; i++) {
                        if (orders[finds[i].toString().charAt(0)] === -1) {
                            orders[finds[i].toString().charAt(0)] = i + 1;
                        }
                    }
                }
                return orders;
            };

            var regstr = '^' + f.toString()
                    .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
                        var ml = match.length;
                        switch (match.charAt(0).toLowerCase()) {
                            case 'h':
                                return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
                            case 'm':
                                return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
                            case 's':
                                return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
                            case 'l':
                                return '(\\d?\\d?\\d)';
                            case 'c':
                                return '(\\d?\\d?\\d)';
                            case 'z':
                                return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
                            case 't':
                                return getPatternAmpm(o.amNames, o.pmNames);
                            default:    // literal escaped in quotes
                                return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';
                        }
                    })
                    .replace(/\s/g, '\\s?') +
                o.timeSuffix + '$',
                order = getFormatPositions(f),
                ampm = '',
                treg;

            treg = s.match(new RegExp(regstr, 'i'));

            var resTime = {
                hour: 0,
                minute: 0,
                second: 0,
                millisec: 0,
                microsec: 0
            };

            if (treg) {
                if (order.t !== -1) {
                    if (treg[order.t] === undefined || treg[order.t].length === 0) {
                        ampm = '';
                        resTime.ampm = '';
                    } else {
                        ampm = $.inArray(treg[order.t].toUpperCase(), $.map(o.amNames, function (x,i) { return x.toUpperCase(); })) !== -1 ? 'AM' : 'PM';
                        resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];
                    }
                }

                if (order.h !== -1) {
                    if (ampm === 'AM' && treg[order.h] === '12') {
                        resTime.hour = 0; // 12am = 0 hour
                    } else {
                        if (ampm === 'PM' && treg[order.h] !== '12') {
                            resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12
                        } else {
                            resTime.hour = Number(treg[order.h]);
                        }
                    }
                }

                if (order.m !== -1) {
                    resTime.minute = Number(treg[order.m]);
                }
                if (order.s !== -1) {
                    resTime.second = Number(treg[order.s]);
                }
                if (order.l !== -1) {
                    resTime.millisec = Number(treg[order.l]);
                }
                if (order.c !== -1) {
                    resTime.microsec = Number(treg[order.c]);
                }
                if (order.z !== -1 && treg[order.z] !== undefined) {
                    resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);
                }


                return resTime;
            }
            return false;
        };// end strictParse

        // First try JS Date, if that fails, use strictParse
        var looseParse = function (f, s, o) {
            try {
                var d = new Date('2012-01-01 ' + s);
                if (isNaN(d.getTime())) {
                    d = new Date('2012-01-01T' + s);
                    if (isNaN(d.getTime())) {
                        d = new Date('01/01/2012 ' + s);
                        if (isNaN(d.getTime())) {
                            throw "Unable to parse time with native Date: " + s;
                        }
                    }
                }

                return {
                    hour: d.getHours(),
                    minute: d.getMinutes(),
                    second: d.getSeconds(),
                    millisec: d.getMilliseconds(),
                    microsec: d.getMicroseconds(),
                    timezone: d.getTimezoneOffset() * -1
                };
            }
            catch (err) {
                try {
                    return strictParse(f, s, o);
                }
                catch (err2) {
                    $.timepicker.log("Unable to parse \ntimeString: " + s + "\ntimeFormat: " + f);
                }
            }
            return false;
        }; // end looseParse

        if (typeof o.parse === "function") {
            return o.parse(timeFormat, timeString, o);
        }
        if (o.parse === 'loose') {
            return looseParse(timeFormat, timeString, o);
        }
        return strictParse(timeFormat, timeString, o);
    };

    /**
     * Public utility to format the time
     * @param {string} format format of the time
     * @param {Object} time Object not a Date for timezones
     * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm
     * @returns {string} the formatted time
     */
    $.datepicker.formatTime = function (format, time, options) {
        options = options || {};
        options = $.extend({}, $.timepicker._defaults, options);
        time = $.extend({
            hour: 0,
            minute: 0,
            second: 0,
            millisec: 0,
            microsec: 0,
            timezone: null
        }, time);

        var tmptime = format,
            ampmName = options.amNames[0],
            hour = parseInt(time.hour, 10);

        if (hour > 11) {
            ampmName = options.pmNames[0];
        }

        tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
            switch (match) {
                case 'HH':
                    return ('0' + hour).slice(-2);
                case 'H':
                    return hour;
                case 'hh':
                    return ('0' + convert24to12(hour)).slice(-2);
                case 'h':
                    return convert24to12(hour);
                case 'mm':
                    return ('0' + time.minute).slice(-2);
                case 'm':
                    return time.minute;
                case 'ss':
                    return ('0' + time.second).slice(-2);
                case 's':
                    return time.second;
                case 'l':
                    return ('00' + time.millisec).slice(-3);
                case 'c':
                    return ('00' + time.microsec).slice(-3);
                case 'z':
                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);
                case 'Z':
                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);
                case 'T':
                    return ampmName.charAt(0).toUpperCase();
                case 'TT':
                    return ampmName.toUpperCase();
                case 't':
                    return ampmName.charAt(0).toLowerCase();
                case 'tt':
                    return ampmName.toLowerCase();
                default:
                    return match.replace(/'/g, "");
            }
        });

        return tmptime;
    };

    /*
    * the bad hack :/ override datepicker so it doesn't close on select
    // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
    */
    $.datepicker._base_selectDate = $.datepicker._selectDate;
    $.datepicker._selectDate = function (id, dateStr) {
        var inst = this._getInst($(id)[0]),
            tp_inst = this._get(inst, 'timepicker'),
            was_inline;

        if (tp_inst && inst.settings.showTimepicker) {
            tp_inst._limitMinMaxDateTime(inst, true);
            was_inline = inst.inline;
            inst.inline = inst.stay_open = true;
            //This way the onSelect handler called from calendarpicker get the full dateTime
            this._base_selectDate(id, dateStr);
            inst.inline = was_inline;
            inst.stay_open = false;
            this._notifyChange(inst);
            this._updateDatepicker(inst);
        } else {
            this._base_selectDate(id, dateStr);
        }
    };

    /*
    * second bad hack :/ override datepicker so it triggers an event when changing the input field
    * and does not redraw the datepicker on every selectDate event
    */
    $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
    $.datepicker._updateDatepicker = function (inst) {

        // don't popup the datepicker if there is another instance already opened
        var input = inst.input[0];
        if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {
            return;
        }

        if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {

            this._base_updateDatepicker(inst);

            // Reload the time control when changing something in the input text field.
            var tp_inst = this._get(inst, 'timepicker');
            if (tp_inst) {
                tp_inst._addTimePicker(inst);
            }
        }
    };

    /*
    * third bad hack :/ override datepicker so it allows spaces and colon in the input field
    */
    $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
    $.datepicker._doKeyPress = function (event) {
        var inst = $.datepicker._getInst(event.target),
            tp_inst = $.datepicker._get(inst, 'timepicker');

        if (tp_inst) {
            if ($.datepicker._get(inst, 'constrainInput')) {
                var ampm = tp_inst.support.ampm,
                    tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,
                    dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
                    datetimeChars = tp_inst._defaults.timeFormat.toString()
                            .replace(/[hms]/g, '')
                            .replace(/TT/g, ampm ? 'APM' : '')
                            .replace(/Tt/g, ampm ? 'AaPpMm' : '')
                            .replace(/tT/g, ampm ? 'AaPpMm' : '')
                            .replace(/T/g, ampm ? 'AP' : '')
                            .replace(/tt/g, ampm ? 'apm' : '')
                            .replace(/t/g, ampm ? 'ap' : '') +
                        " " + tp_inst._defaults.separator +
                        tp_inst._defaults.timeSuffix +
                        (tz ? tp_inst._defaults.timezoneList.join('') : '') +
                        (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +
                        dateChars,
                    chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
                return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
            }
        }

        return $.datepicker._base_doKeyPress(event);
    };

    /*
    * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
    * Update any alternate field to synchronise with the main field.
    */
    $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;
    $.datepicker._updateAlternate = function (inst) {
        var tp_inst = this._get(inst, 'timepicker');
        if (tp_inst) {
            var altField = tp_inst._defaults.altField;
            if (altField) { // update alternate field too
                var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,
                    date = this._getDate(inst),
                    formatCfg = $.datepicker._getFormatConfig(inst),
                    altFormattedDateTime = '',
                    altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,
                    altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,
                    altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;

                altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;
                if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {
                    if (tp_inst._defaults.altFormat) {
                        altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;
                    }
                    else {
                        altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
                    }
                }
                $(altField).val( inst.input.val() ? altFormattedDateTime : "");
            }
        }
        else {
            $.datepicker._base_updateAlternate(inst);
        }
    };

    /*
    * Override key up event to sync manual input changes.
    */
    $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
    $.datepicker._doKeyUp = function (event) {
        var inst = $.datepicker._getInst(event.target),
            tp_inst = $.datepicker._get(inst, 'timepicker');

        if (tp_inst) {
            if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
                try {
                    $.datepicker._updateDatepicker(inst);
                } catch (err) {
                    $.timepicker.log(err);
                }
            }
        }

        return $.datepicker._base_doKeyUp(event);
    };

    /*
    * override "Today" button to also grab the time and set it to input field.
    */
    $.datepicker._base_gotoToday = $.datepicker._gotoToday;
    $.datepicker._gotoToday = function (id) {
        var inst = this._getInst($(id)[0]);
        this._base_gotoToday(id);
        var tp_inst = this._get(inst, 'timepicker');
        if (!tp_inst) {
            return;
        }

        var tzoffset = $.timepicker.timezoneOffsetNumber(tp_inst.timezone);
        var now = new Date();
        now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + parseInt(tzoffset, 10));
        this._setTime(inst, now);
        this._setDate(inst, now);
        tp_inst._onSelectHandler();
    };

    /*
    * Disable & enable the Time in the datetimepicker
    */
    $.datepicker._disableTimepickerDatepicker = function (target) {
        var inst = this._getInst(target);
        if (!inst) {
            return;
        }

        var tp_inst = this._get(inst, 'timepicker');
        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
        if (tp_inst) {
            inst.settings.showTimepicker = false;
            tp_inst._defaults.showTimepicker = false;
            tp_inst._updateDateTime(inst);
        }
    };

    $.datepicker._enableTimepickerDatepicker = function (target) {
        var inst = this._getInst(target);
        if (!inst) {
            return;
        }

        var tp_inst = this._get(inst, 'timepicker');
        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
        if (tp_inst) {
            inst.settings.showTimepicker = true;
            tp_inst._defaults.showTimepicker = true;
            tp_inst._addTimePicker(inst); // Could be disabled on page load
            tp_inst._updateDateTime(inst);
        }
    };

    /*
    * Create our own set time function
    */
    $.datepicker._setTime = function (inst, date) {
        var tp_inst = this._get(inst, 'timepicker');
        if (tp_inst) {
            var defaults = tp_inst._defaults;

            // calling _setTime with no date sets time to defaults
            tp_inst.hour = date ? date.getHours() : defaults.hour;
            tp_inst.minute = date ? date.getMinutes() : defaults.minute;
            tp_inst.second = date ? date.getSeconds() : defaults.second;
            tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
            tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;

            //check if within min/max times..
            tp_inst._limitMinMaxDateTime(inst, true);

            tp_inst._onTimeChange();
            tp_inst._updateDateTime(inst);
        }
    };

    /*
    * Create new public method to set only time, callable as $().datepicker('setTime', date)
    */
    $.datepicker._setTimeDatepicker = function (target, date, withDate) {
        var inst = this._getInst(target);
        if (!inst) {
            return;
        }

        var tp_inst = this._get(inst, 'timepicker');

        if (tp_inst) {
            this._setDateFromField(inst);
            var tp_date;
            if (date) {
                if (typeof date === "string") {
                    tp_inst._parseTime(date, withDate);
                    tp_date = new Date();
                    tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
                    tp_date.setMicroseconds(tp_inst.microsec);
                } else {
                    tp_date = new Date(date.getTime());
                    tp_date.setMicroseconds(date.getMicroseconds());
                }
                if (tp_date.toString() === 'Invalid Date') {
                    tp_date = undefined;
                }
                this._setTime(inst, tp_date);
            }
        }

    };

    /*
    * override setDate() to allow setting time too within Date object
    */
    $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
    $.datepicker._setDateDatepicker = function (target, _date) {
        var inst = this._getInst(target);
        var date = _date;
        if (!inst) {
            return;
        }

        if (typeof(_date) === 'string') {
            date = new Date(_date);
            if (!date.getTime()) {
                this._base_setDateDatepicker.apply(this, arguments);
                date = $(target).datepicker('getDate');
            }
        }

        var tp_inst = this._get(inst, 'timepicker');
        var tp_date;
        if (date instanceof Date) {
            tp_date = new Date(date.getTime());
            tp_date.setMicroseconds(date.getMicroseconds());
        } else {
            tp_date = date;
        }

        // This is important if you are using the timezone option, javascript's Date
        // object will only return the timezone offset for the current locale, so we
        // adjust it accordingly.  If not using timezone option this won't matter..
        // If a timezone is different in tp, keep the timezone as is
        if (tp_inst && tp_date) {
            // look out for DST if tz wasn't specified
            if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
                tp_inst.timezone = tp_date.getTimezoneOffset() * -1;
            }
            date = $.timepicker.timezoneAdjust(date, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()), tp_inst.timezone);
            tp_date = $.timepicker.timezoneAdjust(tp_date, $.timepicker.timezoneOffsetString(-tp_date.getTimezoneOffset()), tp_inst.timezone);
        }

        this._updateDatepicker(inst);
        this._base_setDateDatepicker.apply(this, arguments);
        this._setTimeDatepicker(target, tp_date, true);
    };

    /*
    * override getDate() to allow getting time too within Date object
    */
    $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
    $.datepicker._getDateDatepicker = function (target, noDefault) {
        var inst = this._getInst(target);
        if (!inst) {
            return;
        }

        var tp_inst = this._get(inst, 'timepicker');

        if (tp_inst) {
            // if it hasn't yet been defined, grab from field
            if (inst.lastVal === undefined) {
                this._setDateFromField(inst, noDefault);
            }

            var date = this._getDate(inst);

            var currDT = null;

            if (tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly) {
                currDT = tp_inst.$input.val() + ' ' + tp_inst.$altInput.val();
            }
            else if (tp_inst.$input.get(0).tagName !== 'INPUT' && tp_inst.$altInput) {
                /**
                 * in case the datetimepicker has been applied to a non-input tag for inline UI,
                 * and the user has not configured the plugin to display only time in altInput,
                 * pick current date time from the altInput (and hope for the best, for now, until "ER1" is applied)
                 *
                 * @todo ER1. Since altInput can have a totally difference format, convert it to standard format by reading input format from "altFormat" and "altTimeFormat" option values
                 */
                currDT = tp_inst.$altInput.val();
            }
            else {
                currDT = tp_inst.$input.val();
            }

            if (date && tp_inst._parseTime(currDT, !inst.settings.timeOnly)) {
                date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
                date.setMicroseconds(tp_inst.microsec);

                // This is important if you are using the timezone option, javascript's Date
                // object will only return the timezone offset for the current locale, so we
                // adjust it accordingly.  If not using timezone option this won't matter..
                if (tp_inst.timezone != null) {
                    // look out for DST if tz wasn't specified
                    if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
                        tp_inst.timezone = date.getTimezoneOffset() * -1;
                    }
                    date = $.timepicker.timezoneAdjust(date, tp_inst.timezone, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()));
                }
            }
            return date;
        }
        return this._base_getDateDatepicker(target, noDefault);
    };

    /*
    * override parseDate() because UI 1.8.14 throws an error about "Extra characters"
    * An option in datapicker to ignore extra format characters would be nicer.
    */
    $.datepicker._base_parseDate = $.datepicker.parseDate;
    $.datepicker.parseDate = function (format, value, settings) {
        var date;
        try {
            date = this._base_parseDate(format, value, settings);
        } catch (err) {
            // Hack!  The error message ends with a colon, a space, and
            // the "extra" characters.  We rely on that instead of
            // attempting to perfectly reproduce the parsing algorithm.
            if (err.indexOf(":") >= 0) {
                date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);
                $.timepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);
            } else {
                throw err;
            }
        }
        return date;
    };

    /*
    * override formatDate to set date with time to the input
    */
    $.datepicker._base_formatDate = $.datepicker._formatDate;
    $.datepicker._formatDate = function (inst, day, month, year) {
        var tp_inst = this._get(inst, 'timepicker');
        if (tp_inst) {
            tp_inst._updateDateTime(inst);
            return tp_inst.$input.val();
        }
        return this._base_formatDate(inst);
    };

    /*
    * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
    */
    $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
    $.datepicker._optionDatepicker = function (target, name, value) {
        var inst = this._getInst(target),
            name_clone;
        if (!inst) {
            return null;
        }

        var tp_inst = this._get(inst, 'timepicker');
        if (tp_inst) {
            var min = null,
                max = null,
                onselect = null,
                overrides = tp_inst._defaults.evnts,
                fns = {},
                prop,
                ret,
                oldVal,
                $target;
            if (typeof name === 'string') { // if min/max was set with the string
                if (name === 'minDate' || name === 'minDateTime') {
                    min = value;
                } else if (name === 'maxDate' || name === 'maxDateTime') {
                    max = value;
                } else if (name === 'onSelect') {
                    onselect = value;
                } else if (overrides.hasOwnProperty(name)) {
                    if (typeof (value) === 'undefined') {
                        return overrides[name];
                    }
                    fns[name] = value;
                    name_clone = {}; //empty results in exiting function after overrides updated
                }
            } else if (typeof name === 'object') { //if min/max was set with the JSON
                if (name.minDate) {
                    min = name.minDate;
                } else if (name.minDateTime) {
                    min = name.minDateTime;
                } else if (name.maxDate) {
                    max = name.maxDate;
                } else if (name.maxDateTime) {
                    max = name.maxDateTime;
                }
                for (prop in overrides) {
                    if (overrides.hasOwnProperty(prop) && name[prop]) {
                        fns[prop] = name[prop];
                    }
                }
            }
            for (prop in fns) {
                if (fns.hasOwnProperty(prop)) {
                    overrides[prop] = fns[prop];
                    if (!name_clone) { name_clone = $.extend({}, name); }
                    delete name_clone[prop];
                }
            }
            if (name_clone && isEmptyObject(name_clone)) { return; }
            if (min) { //if min was set
                if (min === 0) {
                    min = new Date();
                } else {
                    min = new Date(min);
                }
                tp_inst._defaults.minDate = min;
                tp_inst._defaults.minDateTime = min;
            } else if (max) { //if max was set
                if (max === 0) {
                    max = new Date();
                } else {
                    max = new Date(max);
                }
                tp_inst._defaults.maxDate = max;
                tp_inst._defaults.maxDateTime = max;
            } else if (onselect) {
                tp_inst._defaults.onSelect = onselect;
            }

            // Datepicker will override our date when we call _base_optionDatepicker when
            // calling minDate/maxDate, so we will first grab the value, call
            // _base_optionDatepicker, then set our value back.
            if(min || max){
                $target = $(target);
                oldVal = $target.datetimepicker('getDate');
                ret = this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
                $target.datetimepicker('setDate', oldVal);
                return ret;
            }
        }
        if (value === undefined) {
            return this._base_optionDatepicker.call($.datepicker, target, name);
        }
        return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
    };

    /*
    * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
    * it will return false for all objects
    */
    var isEmptyObject = function (obj) {
        var prop;
        for (prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                return false;
            }
        }
        return true;
    };

    /*
    * jQuery extend now ignores nulls!
    */
    var extendRemove = function (target, props) {
        $.extend(target, props);
        for (var name in props) {
            if (props[name] === null || props[name] === undefined) {
                target[name] = props[name];
            }
        }
        return target;
    };

    /*
    * Determine by the time format which units are supported
    * Returns an object of booleans for each unit
    */
    var detectSupport = function (timeFormat) {
        var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals
            isIn = function (f, t) { // does the format contain the token?
                return f.indexOf(t) !== -1 ? true : false;
            };
        return {
            hour: isIn(tf, 'h'),
            minute: isIn(tf, 'm'),
            second: isIn(tf, 's'),
            millisec: isIn(tf, 'l'),
            microsec: isIn(tf, 'c'),
            timezone: isIn(tf, 'z'),
            ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),
            iso8601: isIn(timeFormat, 'Z')
        };
    };

    /*
    * Converts 24 hour format into 12 hour
    * Returns 12 hour without leading 0
    */
    var convert24to12 = function (hour) {
        hour %= 12;

        if (hour === 0) {
            hour = 12;
        }

        return String(hour);
    };

    var computeEffectiveSetting = function (settings, property) {
        return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];
    };

    /*
    * Splits datetime string into date and time substrings.
    * Throws exception when date can't be parsed
    * Returns {dateString: dateString, timeString: timeString}
    */
    var splitDateTime = function (dateTimeString, timeSettings) {
        // The idea is to get the number separator occurrences in datetime and the time format requested (since time has
        // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
        var separator = computeEffectiveSetting(timeSettings, 'separator'),
            format = computeEffectiveSetting(timeSettings, 'timeFormat'),
            timeParts = format.split(separator), // how many occurrences of separator may be in our format?
            timePartsLen = timeParts.length,
            allParts = dateTimeString.split(separator),
            allPartsLen = allParts.length;

        if (allPartsLen > 1) {
            return {
                dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),
                timeString: allParts.splice(0, timePartsLen).join(separator)
            };
        }

        return {
            dateString: dateTimeString,
            timeString: ''
        };
    };

    /*
    * Internal function to parse datetime interval
    * Returns: {date: Date, timeObj: Object}, where
    *   date - parsed date without time (type Date)
    *   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional
    */
    var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
        var date,
            parts,
            parsedTime;

        parts = splitDateTime(dateTimeString, timeSettings);
        date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);

        if (parts.timeString === '') {
            return {
                date: date
            };
        }

        parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);

        if (!parsedTime) {
            throw 'Wrong time format';
        }

        return {
            date: date,
            timeObj: parsedTime
        };
    };

    /*
    * Internal function to set timezone_select to the local timezone
    */
    var selectLocalTimezone = function (tp_inst, date) {
        if (tp_inst && tp_inst.timezone_select) {
            var now = date || new Date();
            tp_inst.timezone_select.val(-now.getTimezoneOffset());
        }
    };

    /*
    * Create a Singleton Instance
    */
    $.timepicker = new Timepicker();

    /**
     * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
     * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned
     * @param {boolean} iso8601 if true formats in accordance to iso8601 "+12:45"
     * @return {string}
     */
    $.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {
        if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {
            return tzMinutes;
        }

        var off = tzMinutes,
            minutes = off % 60,
            hours = (off - minutes) / 60,
            iso = iso8601 ? ':' : '',
            tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);

        if (tz === '+00:00') {
            return 'Z';
        }
        return tz;
    };

    /**
     * Get the number in minutes that represents a timezone string
     * @param  {string} tzString formatted like "+0500", "-1245", "Z"
     * @return {number} the offset minutes or the original string if it doesn't match expectations
     */
    $.timepicker.timezoneOffsetNumber = function (tzString) {
        var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with "+1245"

        if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset
            return 0;
        }

        if (!/^(\-|\+)\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back
            return parseInt(tzString, 10);
        }

        return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus
            ((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)
                parseInt(normalized.substr(3, 2), 10))); // minutes
    };

    /**
     * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)
     * @param  {Date} date
     * @param  {string} fromTimezone formatted like "+0500", "-1245"
     * @param  {string} toTimezone formatted like "+0500", "-1245"
     * @return {Date}
     */
    $.timepicker.timezoneAdjust = function (date, fromTimezone, toTimezone) {
        var fromTz = $.timepicker.timezoneOffsetNumber(fromTimezone);
        var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);
        if (!isNaN(toTz)) {
            date.setMinutes(date.getMinutes() + (-fromTz) - (-toTz));
        }
        return date;
    };

    /**
     * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to
     * enforce date range limits.
     * n.b. The input value must be correctly formatted (reformatting is not supported)
     * @param  {Element} startTime
     * @param  {Element} endTime
     * @param  {Object} options Options for the timepicker() call
     * @return {jQuery}
     */
    $.timepicker.timeRange = function (startTime, endTime, options) {
        return $.timepicker.handleRange('timepicker', startTime, endTime, options);
    };

    /**
     * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
     * enforce date range limits.
     * @param  {Element} startTime
     * @param  {Element} endTime
     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
     *   a boolean value that can be used to reformat the input values to the `dateFormat`.
     * @param  {string} method Can be used to specify the type of picker to be added
     * @return {jQuery}
     */
    $.timepicker.datetimeRange = function (startTime, endTime, options) {
        $.timepicker.handleRange('datetimepicker', startTime, endTime, options);
    };

    /**
     * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to
     * enforce date range limits.
     * @param  {Element} startTime
     * @param  {Element} endTime
     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
     *   a boolean value that can be used to reformat the input values to the `dateFormat`.
     * @return {jQuery}
     */
    $.timepicker.dateRange = function (startTime, endTime, options) {
        $.timepicker.handleRange('datepicker', startTime, endTime, options);
    };

    /**
     * Calls `method` on the `startTime` and `endTime` elements, and configures them to
     * enforce date range limits.
     * @param  {string} method Can be used to specify the type of picker to be added
     * @param  {Element} startTime
     * @param  {Element} endTime
     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
     *   a boolean value that can be used to reformat the input values to the `dateFormat`.
     * @return {jQuery}
     */
    $.timepicker.handleRange = function (method, startTime, endTime, options) {
        options = $.extend({}, {
            minInterval: 0, // min allowed interval in milliseconds
            maxInterval: 0, // max allowed interval in milliseconds
            start: {},      // options for start picker
            end: {}         // options for end picker
        }, options);

        // for the mean time this fixes an issue with calling getDate with timepicker()
        var timeOnly = false;
        if(method === 'timepicker'){
            timeOnly = true;
            method = 'datetimepicker';
        }

        function checkDates(changed, other) {
            var startdt = startTime[method]('getDate'),
                enddt = endTime[method]('getDate'),
                changeddt = changed[method]('getDate');

            if (startdt !== null) {
                var minDate = new Date(startdt.getTime()),
                    maxDate = new Date(startdt.getTime());

                minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);
                maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);

                if (options.minInterval > 0 && minDate > enddt) { // minInterval check
                    endTime[method]('setDate', minDate);
                }
                else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check
                    endTime[method]('setDate', maxDate);
                }
                else if (startdt > enddt) {
                    other[method]('setDate', changeddt);
                }
            }
        }

        function selected(changed, other, option) {
            if (!changed.val()) {
                return;
            }
            var date = changed[method].call(changed, 'getDate');
            if (date !== null && options.minInterval > 0) {
                if (option === 'minDate') {
                    date.setMilliseconds(date.getMilliseconds() + options.minInterval);
                }
                if (option === 'maxDate') {
                    date.setMilliseconds(date.getMilliseconds() - options.minInterval);
                }
            }

            if (date.getTime) {
                other[method].call(other, 'option', option, date);
            }
        }

        $.fn[method].call(startTime, $.extend({
            timeOnly: timeOnly,
            onClose: function (dateText, inst) {
                checkDates($(this), endTime);
            },
            onSelect: function (selectedDateTime) {
                selected($(this), endTime, 'minDate');
            }
        }, options, options.start));
        $.fn[method].call(endTime, $.extend({
            timeOnly: timeOnly,
            onClose: function (dateText, inst) {
                checkDates($(this), startTime);
            },
            onSelect: function (selectedDateTime) {
                selected($(this), startTime, 'maxDate');
            }
        }, options, options.end));

        checkDates(startTime, endTime);

        selected(startTime, endTime, 'minDate');
        selected(endTime, startTime, 'maxDate');

        return $([startTime.get(0), endTime.get(0)]);
    };

    /**
     * Log error or data to the console during error or debugging
     * @param  {Object} err pass any type object to log to the console during error or debugging
     * @return {void}
     */
    $.timepicker.log = function () {
        // Older IE (9, maybe 10) throw error on accessing `window.console.log.apply`, so check first.
        if (window.console && window.console.log && window.console.log.apply) {
            window.console.log.apply(window.console, Array.prototype.slice.call(arguments));
        }
    };

    /*
     * Add util object to allow access to private methods for testability.
     */
    $.timepicker._util = {
        _extendRemove: extendRemove,
        _isEmptyObject: isEmptyObject,
        _convert24to12: convert24to12,
        _detectSupport: detectSupport,
        _selectLocalTimezone: selectLocalTimezone,
        _computeEffectiveSetting: computeEffectiveSetting,
        _splitDateTime: splitDateTime,
        _parseDateTimeInternal: parseDateTimeInternal
    };

    /*
    * Microsecond support
    */
    if (!Date.prototype.getMicroseconds) {
        Date.prototype.microseconds = 0;
        Date.prototype.getMicroseconds = function () { return this.microseconds; };
        Date.prototype.setMicroseconds = function (m) {
            this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));
            this.microseconds = m % 1000;
            return this;
        };
    }

    /*
    * Keep up with the version
    */
    $.timepicker.version = "1.6.3";

}));

/**
 * 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'
    });
});

/*!
 * 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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/draggable',[
            "jquery",
            "./mouse",
            "../data",
            "../plugin",
            "../safe-active-element",
            "../safe-blur",
            "../scroll-parent",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.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 );
            }
        }
    } );

    return $.ui.draggable;

} );

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

//>>label: Droppable
//>>group: Interactions
//>>description: Enables drop targets for draggable elements.
//>>docs: http://api.jqueryui.com/droppable/
//>>demos: http://jqueryui.com/droppable/

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/droppable',[
            "jquery",
            "./draggable",
            "./mouse",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.widget( "ui.droppable", {
        version: "1.13.2",
        widgetEventPrefix: "drop",
        options: {
            accept: "*",
            addClasses: true,
            greedy: false,
            scope: "default",
            tolerance: "intersect",

            // Callbacks
            activate: null,
            deactivate: null,
            drop: null,
            out: null,
            over: null
        },
        _create: function() {

            var proportions,
                o = this.options,
                accept = o.accept;

            this.isover = false;
            this.isout = true;

            this.accept = typeof accept === "function" ? accept : function( d ) {
                return d.is( accept );
            };

            this.proportions = function( /* valueToWrite */ ) {
                if ( arguments.length ) {

                    // Store the droppable's proportions
                    proportions = arguments[ 0 ];
                } else {

                    // Retrieve or derive the droppable's proportions
                    return proportions ?
                        proportions :
                        proportions = {
                            width: this.element[ 0 ].offsetWidth,
                            height: this.element[ 0 ].offsetHeight
                        };
                }
            };

            this._addToManager( o.scope );

            if ( o.addClasses ) {
                this._addClass( "ui-droppable" );
            }

        },

        _addToManager: function( scope ) {

            // Add the reference and positions to the manager
            $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
            $.ui.ddmanager.droppables[ scope ].push( this );
        },

        _splice: function( drop ) {
            var i = 0;
            for ( ; i < drop.length; i++ ) {
                if ( drop[ i ] === this ) {
                    drop.splice( i, 1 );
                }
            }
        },

        _destroy: function() {
            var drop = $.ui.ddmanager.droppables[ this.options.scope ];

            this._splice( drop );
        },

        _setOption: function( key, value ) {

            if ( key === "accept" ) {
                this.accept = typeof value === "function" ? value : function( d ) {
                    return d.is( value );
                };
            } else if ( key === "scope" ) {
                var drop = $.ui.ddmanager.droppables[ this.options.scope ];

                this._splice( drop );
                this._addToManager( value );
            }

            this._super( key, value );
        },

        _activate: function( event ) {
            var draggable = $.ui.ddmanager.current;

            this._addActiveClass();
            if ( draggable ) {
                this._trigger( "activate", event, this.ui( draggable ) );
            }
        },

        _deactivate: function( event ) {
            var draggable = $.ui.ddmanager.current;

            this._removeActiveClass();
            if ( draggable ) {
                this._trigger( "deactivate", event, this.ui( draggable ) );
            }
        },

        _over: function( event ) {

            var draggable = $.ui.ddmanager.current;

            // Bail if draggable and droppable are same element
            if ( !draggable || ( draggable.currentItem ||
                draggable.element )[ 0 ] === this.element[ 0 ] ) {
                return;
            }

            if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
                draggable.element ) ) ) {
                this._addHoverClass();
                this._trigger( "over", event, this.ui( draggable ) );
            }

        },

        _out: function( event ) {

            var draggable = $.ui.ddmanager.current;

            // Bail if draggable and droppable are same element
            if ( !draggable || ( draggable.currentItem ||
                draggable.element )[ 0 ] === this.element[ 0 ] ) {
                return;
            }

            if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
                draggable.element ) ) ) {
                this._removeHoverClass();
                this._trigger( "out", event, this.ui( draggable ) );
            }

        },

        _drop: function( event, custom ) {

            var draggable = custom || $.ui.ddmanager.current,
                childrenIntersection = false;

            // Bail if draggable and droppable are same element
            if ( !draggable || ( draggable.currentItem ||
                draggable.element )[ 0 ] === this.element[ 0 ] ) {
                return false;
            }

            this.element
                .find( ":data(ui-droppable)" )
                .not( ".ui-draggable-dragging" )
                .each( function() {
                    var inst = $( this ).droppable( "instance" );
                    if (
                        inst.options.greedy &&
                        !inst.options.disabled &&
                        inst.options.scope === draggable.options.scope &&
                        inst.accept.call(
                            inst.element[ 0 ], ( draggable.currentItem || draggable.element )
                        ) &&
                        $.ui.intersect(
                            draggable,
                            $.extend( inst, { offset: inst.element.offset() } ),
                            inst.options.tolerance, event
                        )
                    ) {
                        childrenIntersection = true;
                        return false;
                    }
                } );
            if ( childrenIntersection ) {
                return false;
            }

            if ( this.accept.call( this.element[ 0 ],
                ( draggable.currentItem || draggable.element ) ) ) {
                this._removeActiveClass();
                this._removeHoverClass();

                this._trigger( "drop", event, this.ui( draggable ) );
                return this.element;
            }

            return false;

        },

        ui: function( c ) {
            return {
                draggable: ( c.currentItem || c.element ),
                helper: c.helper,
                position: c.position,
                offset: c.positionAbs
            };
        },

        // Extension points just to make backcompat sane and avoid duplicating logic
        // TODO: Remove in 1.14 along with call to it below
        _addHoverClass: function() {
            this._addClass( "ui-droppable-hover" );
        },

        _removeHoverClass: function() {
            this._removeClass( "ui-droppable-hover" );
        },

        _addActiveClass: function() {
            this._addClass( "ui-droppable-active" );
        },

        _removeActiveClass: function() {
            this._removeClass( "ui-droppable-active" );
        }
    } );

    $.ui.intersect = ( function() {
        function isOverAxis( x, reference, size ) {
            return ( x >= reference ) && ( x < ( reference + size ) );
        }

        return function( draggable, droppable, toleranceMode, event ) {

            if ( !droppable.offset ) {
                return false;
            }

            var x1 = ( draggable.positionAbs ||
                    draggable.position.absolute ).left + draggable.margins.left,
                y1 = ( draggable.positionAbs ||
                    draggable.position.absolute ).top + draggable.margins.top,
                x2 = x1 + draggable.helperProportions.width,
                y2 = y1 + draggable.helperProportions.height,
                l = droppable.offset.left,
                t = droppable.offset.top,
                r = l + droppable.proportions().width,
                b = t + droppable.proportions().height;

            switch ( toleranceMode ) {
                case "fit":
                    return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
                case "intersect":
                    return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
                        x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
                        t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
                        y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
                case "pointer":
                    return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
                        isOverAxis( event.pageX, l, droppable.proportions().width );
                case "touch":
                    return (
                        ( y1 >= t && y1 <= b ) || // Top edge touching
                        ( y2 >= t && y2 <= b ) || // Bottom edge touching
                        ( y1 < t && y2 > b ) // Surrounded vertically
                    ) && (
                        ( x1 >= l && x1 <= r ) || // Left edge touching
                        ( x2 >= l && x2 <= r ) || // Right edge touching
                        ( x1 < l && x2 > r ) // Surrounded horizontally
                    );
                default:
                    return false;
            }
        };
    } )();

    /*
        This manager tracks offsets of draggables and droppables
    */
    $.ui.ddmanager = {
        current: null,
        droppables: { "default": [] },
        prepareOffsets: function( t, event ) {

            var i, j,
                m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
                type = event ? event.type : null, // workaround for #2317
                list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();

            droppablesLoop: for ( i = 0; i < m.length; i++ ) {

                // No disabled and non-accepted
                if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
                    ( t.currentItem || t.element ) ) ) ) {
                    continue;
                }

                // Filter out elements in the current dragged item
                for ( j = 0; j < list.length; j++ ) {
                    if ( list[ j ] === m[ i ].element[ 0 ] ) {
                        m[ i ].proportions().height = 0;
                        continue droppablesLoop;
                    }
                }

                m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
                if ( !m[ i ].visible ) {
                    continue;
                }

                // Activate the droppable if used directly from draggables
                if ( type === "mousedown" ) {
                    m[ i ]._activate.call( m[ i ], event );
                }

                m[ i ].offset = m[ i ].element.offset();
                m[ i ].proportions( {
                    width: m[ i ].element[ 0 ].offsetWidth,
                    height: m[ i ].element[ 0 ].offsetHeight
                } );

            }

        },
        drop: function( draggable, event ) {

            var dropped = false;

            // Create a copy of the droppables in case the list changes during the drop (#9116)
            $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {

                if ( !this.options ) {
                    return;
                }
                if ( !this.options.disabled && this.visible &&
                    $.ui.intersect( draggable, this, this.options.tolerance, event ) ) {
                    dropped = this._drop.call( this, event ) || dropped;
                }

                if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
                    ( draggable.currentItem || draggable.element ) ) ) {
                    this.isout = true;
                    this.isover = false;
                    this._deactivate.call( this, event );
                }

            } );
            return dropped;

        },
        dragStart: function( draggable, event ) {

            // Listen for scrolling so that if the dragging causes scrolling the position of the
            // droppables can be recalculated (see #5003)
            draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
                if ( !draggable.options.refreshPositions ) {
                    $.ui.ddmanager.prepareOffsets( draggable, event );
                }
            } );
        },
        drag: function( draggable, event ) {

            // If you have a highly dynamic page, you might try this option. It renders positions
            // every time you move the mouse.
            if ( draggable.options.refreshPositions ) {
                $.ui.ddmanager.prepareOffsets( draggable, event );
            }

            // Run through all droppables and check their positions based on specific tolerance options
            $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {

                if ( this.options.disabled || this.greedyChild || !this.visible ) {
                    return;
                }

                var parentInstance, scope, parent,
                    intersects = $.ui.intersect( draggable, this, this.options.tolerance, event ),
                    c = !intersects && this.isover ?
                        "isout" :
                        ( intersects && !this.isover ? "isover" : null );
                if ( !c ) {
                    return;
                }

                if ( this.options.greedy ) {

                    // find droppable parents with same scope
                    scope = this.options.scope;
                    parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
                        return $( this ).droppable( "instance" ).options.scope === scope;
                    } );

                    if ( parent.length ) {
                        parentInstance = $( parent[ 0 ] ).droppable( "instance" );
                        parentInstance.greedyChild = ( c === "isover" );
                    }
                }

                // We just moved into a greedy child
                if ( parentInstance && c === "isover" ) {
                    parentInstance.isover = false;
                    parentInstance.isout = true;
                    parentInstance._out.call( parentInstance, event );
                }

                this[ c ] = true;
                this[ c === "isout" ? "isover" : "isout" ] = false;
                this[ c === "isover" ? "_over" : "_out" ].call( this, event );

                // We just moved out of a greedy child
                if ( parentInstance && c === "isout" ) {
                    parentInstance.isout = false;
                    parentInstance.isover = true;
                    parentInstance._over.call( parentInstance, event );
                }
            } );

        },
        dragStop: function( draggable, event ) {
            draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );

            // Call prepareOffsets one final time since IE does not fire return scroll events when
            // overflow was caused by drag (see #5003)
            if ( !draggable.options.refreshPositions ) {
                $.ui.ddmanager.prepareOffsets( draggable, event );
            }
        }
    };

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
    if ( $.uiBackCompat !== false ) {

        // Backcompat for activeClass and hoverClass options
        $.widget( "ui.droppable", $.ui.droppable, {
            options: {
                hoverClass: false,
                activeClass: false
            },
            _addActiveClass: function() {
                this._super();
                if ( this.options.activeClass ) {
                    this.element.addClass( this.options.activeClass );
                }
            },
            _removeActiveClass: function() {
                this._super();
                if ( this.options.activeClass ) {
                    this.element.removeClass( this.options.activeClass );
                }
            },
            _addHoverClass: function() {
                this._super();
                if ( this.options.hoverClass ) {
                    this.element.addClass( this.options.hoverClass );
                }
            },
            _removeHoverClass: function() {
                this._super();
                if ( this.options.hoverClass ) {
                    this.element.removeClass( this.options.hoverClass );
                }
            }
        } );
    }

    return $.ui.droppable;

} );

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

define('mage/trim-input',[
    'jquery'
], function ($) {
    'use strict';

    $.widget('mage.trimInput', {
        options: {
            cache: {}
        },

        /**
         * Widget initialization
         * @private
         */
        _create: function () {
            this.options.cache.input = $(this.element);
            this._bind();
        },

        /**
         * Event binding, will monitor change, keyup and paste events.
         * @private
         */
        _bind: function () {
            if (this.options.cache.input.length) {
                this._on(this.options.cache.input, {
                    'change': this._trimInput,
                    'keyup': this._trimInput,
                    'paste': this._trimInput
                });
            }
        },

        /**
         * Trim value
         * @private
         */
        _trimInput: function () {
            // Safari caret position workaround: storing carter position
            var caretStart, caretEnd, input;

            caretStart = this.options.cache.input.get(0).selectionStart;
            caretEnd = this.options.cache.input.get(0).selectionEnd;

            input = this._getInputValue().trim();

            this.options.cache.input.val(input);

            // Safari caret position workaround: setting caret position to previously stored values
            if (caretStart !== null && caretEnd !== null) {
                this.options.cache.input.get(0).setSelectionRange(caretStart, caretEnd);
            }
        },

        /**
         * Get input value
         * @returns {*}
         * @private
         */
        _getInputValue: function () {
            return this.options.cache.input.val();
        }
    });

    return $.mage.trimInput;
});

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

//>>label: Autocomplete
//>>group: Widgets
//>>description: Lists suggested words as the user is typing.
//>>docs: http://api.jqueryui.com/autocomplete/
//>>demos: http://jqueryui.com/autocomplete/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/autocomplete.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/autocomplete',[
            "jquery",
            "./menu",
            "../keycode",
            "../position",
            "../safe-active-element",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.widget( "ui.autocomplete", {
        version: "1.13.2",
        defaultElement: "<input>",
        options: {
            appendTo: null,
            autoFocus: false,
            delay: 300,
            minLength: 1,
            position: {
                my: "left top",
                at: "left bottom",
                collision: "none"
            },
            source: null,

            // Callbacks
            change: null,
            close: null,
            focus: null,
            open: null,
            response: null,
            search: null,
            select: null
        },

        requestIndex: 0,
        pending: 0,
        liveRegionTimer: null,

        _create: function() {

            // Some browsers only repeat keydown events, not keypress events,
            // so we use the suppressKeyPress flag to determine if we've already
            // handled the keydown event. #7269
            // Unfortunately the code for & in keypress is the same as the up arrow,
            // so we use the suppressKeyPressRepeat flag to avoid handling keypress
            // events when we know the keydown event was used to modify the
            // search term. #7799
            var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
                nodeName = this.element[ 0 ].nodeName.toLowerCase(),
                isTextarea = nodeName === "textarea",
                isInput = nodeName === "input";

            // Textareas are always multi-line
            // Inputs are always single-line, even if inside a contentEditable element
            // IE also treats inputs as contentEditable
            // All other element types are determined by whether or not they're contentEditable
            this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );

            this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
            this.isNewMenu = true;

            this._addClass( "ui-autocomplete-input" );
            this.element.attr( "autocomplete", "off" );

            this._on( this.element, {
                keydown: function( event ) {
                    if ( this.element.prop( "readOnly" ) ) {
                        suppressKeyPress = true;
                        suppressInput = true;
                        suppressKeyPressRepeat = true;
                        return;
                    }

                    suppressKeyPress = false;
                    suppressInput = false;
                    suppressKeyPressRepeat = false;
                    var keyCode = $.ui.keyCode;
                    switch ( event.keyCode ) {
                        case keyCode.PAGE_UP:
                            suppressKeyPress = true;
                            this._move( "previousPage", event );
                            break;
                        case keyCode.PAGE_DOWN:
                            suppressKeyPress = true;
                            this._move( "nextPage", event );
                            break;
                        case keyCode.UP:
                            suppressKeyPress = true;
                            this._keyEvent( "previous", event );
                            break;
                        case keyCode.DOWN:
                            suppressKeyPress = true;
                            this._keyEvent( "next", event );
                            break;
                        case keyCode.ENTER:

                            // when menu is open and has focus
                            if ( this.menu.active ) {

                                // #6055 - Opera still allows the keypress to occur
                                // which causes forms to submit
                                suppressKeyPress = true;
                                event.preventDefault();
                                this.menu.select( event );
                            }
                            break;
                        case keyCode.TAB:
                            if ( this.menu.active ) {
                                this.menu.select( event );
                            }
                            break;
                        case keyCode.ESCAPE:
                            if ( this.menu.element.is( ":visible" ) ) {
                                if ( !this.isMultiLine ) {
                                    this._value( this.term );
                                }
                                this.close( event );

                                // Different browsers have different default behavior for escape
                                // Single press can mean undo or clear
                                // Double press in IE means clear the whole form
                                event.preventDefault();
                            }
                            break;
                        default:
                            suppressKeyPressRepeat = true;

                            // search timeout should be triggered before the input value is changed
                            this._searchTimeout( event );
                            break;
                    }
                },
                keypress: function( event ) {
                    if ( suppressKeyPress ) {
                        suppressKeyPress = false;
                        if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
                            event.preventDefault();
                        }
                        return;
                    }
                    if ( suppressKeyPressRepeat ) {
                        return;
                    }

                    // Replicate some key handlers to allow them to repeat in Firefox and Opera
                    var keyCode = $.ui.keyCode;
                    switch ( event.keyCode ) {
                        case keyCode.PAGE_UP:
                            this._move( "previousPage", event );
                            break;
                        case keyCode.PAGE_DOWN:
                            this._move( "nextPage", event );
                            break;
                        case keyCode.UP:
                            this._keyEvent( "previous", event );
                            break;
                        case keyCode.DOWN:
                            this._keyEvent( "next", event );
                            break;
                    }
                },
                input: function( event ) {
                    if ( suppressInput ) {
                        suppressInput = false;
                        event.preventDefault();
                        return;
                    }
                    this._searchTimeout( event );
                },
                focus: function() {
                    this.selectedItem = null;
                    this.previous = this._value();
                },
                blur: function( event ) {
                    clearTimeout( this.searching );
                    this.close( event );
                    this._change( event );
                }
            } );

            this._initSource();
            this.menu = $( "<ul>" )
                .appendTo( this._appendTo() )
                .menu( {

                    // disable ARIA support, the live region takes care of that
                    role: null
                } )
                .hide()

                // Support: IE 11 only, Edge <= 14
                // For other browsers, we preventDefault() on the mousedown event
                // to keep the dropdown from taking focus from the input. This doesn't
                // work for IE/Edge, causing problems with selection and scrolling (#9638)
                // Happily, IE and Edge support an "unselectable" attribute that
                // prevents an element from receiving focus, exactly what we want here.
                .attr( {
                    "unselectable": "on"
                } )
                .menu( "instance" );

            this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
            this._on( this.menu.element, {
                mousedown: function( event ) {

                    // Prevent moving focus out of the text field
                    event.preventDefault();
                },
                menufocus: function( event, ui ) {
                    var label, item;

                    // support: Firefox
                    // Prevent accidental activation of menu items in Firefox (#7024 #9118)
                    if ( this.isNewMenu ) {
                        this.isNewMenu = false;
                        if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
                            this.menu.blur();

                            this.document.one( "mousemove", function() {
                                $( event.target ).trigger( event.originalEvent );
                            } );

                            return;
                        }
                    }

                    item = ui.item.data( "ui-autocomplete-item" );
                    if ( false !== this._trigger( "focus", event, { item: item } ) ) {

                        // use value to match what will end up in the input, if it was a key event
                        if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
                            this._value( item.value );
                        }
                    }

                    // Announce the value in the liveRegion
                    label = ui.item.attr( "aria-label" ) || item.value;
                    if ( label && String.prototype.trim.call( label ).length ) {
                        clearTimeout( this.liveRegionTimer );
                        this.liveRegionTimer = this._delay( function() {
                            this.liveRegion.html( $( "<div>" ).text( label ) );
                        }, 100 );
                    }
                },
                menuselect: function( event, ui ) {
                    var item = ui.item.data( "ui-autocomplete-item" ),
                        previous = this.previous;

                    // Only trigger when focus was lost (click on menu)
                    if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
                        this.element.trigger( "focus" );
                        this.previous = previous;

                        // #6109 - IE triggers two focus events and the second
                        // is asynchronous, so we need to reset the previous
                        // term synchronously and asynchronously :-(
                        this._delay( function() {
                            this.previous = previous;
                            this.selectedItem = item;
                        } );
                    }

                    if ( false !== this._trigger( "select", event, { item: item } ) ) {
                        this._value( item.value );
                    }

                    // reset the term after the select event
                    // this allows custom select handling to work properly
                    this.term = this._value();

                    this.close( event );
                    this.selectedItem = item;
                }
            } );

            this.liveRegion = $( "<div>", {
                role: "status",
                "aria-live": "assertive",
                "aria-relevant": "additions"
            } )
                .appendTo( this.document[ 0 ].body );

            this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );

            // Turning off autocomplete prevents the browser from remembering the
            // value when navigating through history, so we re-enable autocomplete
            // if the page is unloaded before the widget is destroyed. #7790
            this._on( this.window, {
                beforeunload: function() {
                    this.element.removeAttr( "autocomplete" );
                }
            } );
        },

        _destroy: function() {
            clearTimeout( this.searching );
            this.element.removeAttr( "autocomplete" );
            this.menu.element.remove();
            this.liveRegion.remove();
        },

        _setOption: function( key, value ) {
            this._super( key, value );
            if ( key === "source" ) {
                this._initSource();
            }
            if ( key === "appendTo" ) {
                this.menu.element.appendTo( this._appendTo() );
            }
            if ( key === "disabled" && value && this.xhr ) {
                this.xhr.abort();
            }
        },

        _isEventTargetInWidget: function( event ) {
            var menuElement = this.menu.element[ 0 ];

            return event.target === this.element[ 0 ] ||
                event.target === menuElement ||
                $.contains( menuElement, event.target );
        },

        _closeOnClickOutside: function( event ) {
            if ( !this._isEventTargetInWidget( event ) ) {
                this.close();
            }
        },

        _appendTo: function() {
            var element = this.options.appendTo;

            if ( element ) {
                element = element.jquery || element.nodeType ?
                    $( element ) :
                    this.document.find( element ).eq( 0 );
            }

            if ( !element || !element[ 0 ] ) {
                element = this.element.closest( ".ui-front, dialog" );
            }

            if ( !element.length ) {
                element = this.document[ 0 ].body;
            }

            return element;
        },

        _initSource: function() {
            var array, url,
                that = this;
            if ( Array.isArray( this.options.source ) ) {
                array = this.options.source;
                this.source = function( request, response ) {
                    response( $.ui.autocomplete.filter( array, request.term ) );
                };
            } else if ( typeof this.options.source === "string" ) {
                url = this.options.source;
                this.source = function( request, response ) {
                    if ( that.xhr ) {
                        that.xhr.abort();
                    }
                    that.xhr = $.ajax( {
                        url: url,
                        data: request,
                        dataType: "json",
                        success: function( data ) {
                            response( data );
                        },
                        error: function() {
                            response( [] );
                        }
                    } );
                };
            } else {
                this.source = this.options.source;
            }
        },

        _searchTimeout: function( event ) {
            clearTimeout( this.searching );
            this.searching = this._delay( function() {

                // Search if the value has changed, or if the user retypes the same value (see #7434)
                var equalValues = this.term === this._value(),
                    menuVisible = this.menu.element.is( ":visible" ),
                    modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;

                if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
                    this.selectedItem = null;
                    this.search( null, event );
                }
            }, this.options.delay );
        },

        search: function( value, event ) {
            value = value != null ? value : this._value();

            // Always save the actual value, not the one passed as an argument
            this.term = this._value();

            if ( value.length < this.options.minLength ) {
                return this.close( event );
            }

            if ( this._trigger( "search", event ) === false ) {
                return;
            }

            return this._search( value );
        },

        _search: function( value ) {
            this.pending++;
            this._addClass( "ui-autocomplete-loading" );
            this.cancelSearch = false;

            this.source( { term: value }, this._response() );
        },

        _response: function() {
            var index = ++this.requestIndex;

            return function( content ) {
                if ( index === this.requestIndex ) {
                    this.__response( content );
                }

                this.pending--;
                if ( !this.pending ) {
                    this._removeClass( "ui-autocomplete-loading" );
                }
            }.bind( this );
        },

        __response: function( content ) {
            if ( content ) {
                content = this._normalize( content );
            }
            this._trigger( "response", null, { content: content } );
            if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
                this._suggest( content );
                this._trigger( "open" );
            } else {

                // use ._close() instead of .close() so we don't cancel future searches
                this._close();
            }
        },

        close: function( event ) {
            this.cancelSearch = true;
            this._close( event );
        },

        _close: function( event ) {

            // Remove the handler that closes the menu on outside clicks
            this._off( this.document, "mousedown" );

            if ( this.menu.element.is( ":visible" ) ) {
                this.menu.element.hide();
                this.menu.blur();
                this.isNewMenu = true;
                this._trigger( "close", event );
            }
        },

        _change: function( event ) {
            if ( this.previous !== this._value() ) {
                this._trigger( "change", event, { item: this.selectedItem } );
            }
        },

        _normalize: function( items ) {

            // assume all items have the right format when the first item is complete
            if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
                return items;
            }
            return $.map( items, function( item ) {
                if ( typeof item === "string" ) {
                    return {
                        label: item,
                        value: item
                    };
                }
                return $.extend( {}, item, {
                    label: item.label || item.value,
                    value: item.value || item.label
                } );
            } );
        },

        _suggest: function( items ) {
            var ul = this.menu.element.empty();
            this._renderMenu( ul, items );
            this.isNewMenu = true;
            this.menu.refresh();

            // Size and position menu
            ul.show();
            this._resizeMenu();
            ul.position( $.extend( {
                of: this.element
            }, this.options.position ) );

            if ( this.options.autoFocus ) {
                this.menu.next();
            }

            // Listen for interactions outside of the widget (#6642)
            this._on( this.document, {
                mousedown: "_closeOnClickOutside"
            } );
        },

        _resizeMenu: function() {
            var ul = this.menu.element;
            ul.outerWidth( Math.max(

                // Firefox wraps long text (possibly a rounding bug)
                // so we add 1px to avoid the wrapping (#7513)
                ul.width( "" ).outerWidth() + 1,
                this.element.outerWidth()
            ) );
        },

        _renderMenu: function( ul, items ) {
            var that = this;
            $.each( items, function( index, item ) {
                that._renderItemData( ul, item );
            } );
        },

        _renderItemData: function( ul, item ) {
            return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
        },

        _renderItem: function( ul, item ) {
            return $( "<li>" )
                .append( $( "<div>" ).text( item.label ) )
                .appendTo( ul );
        },

        _move: function( direction, event ) {
            if ( !this.menu.element.is( ":visible" ) ) {
                this.search( null, event );
                return;
            }
            if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
                this.menu.isLastItem() && /^next/.test( direction ) ) {

                if ( !this.isMultiLine ) {
                    this._value( this.term );
                }

                this.menu.blur();
                return;
            }
            this.menu[ direction ]( event );
        },

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

        _value: function() {
            return this.valueMethod.apply( this.element, arguments );
        },

        _keyEvent: function( keyEvent, event ) {
            if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
                this._move( keyEvent, event );

                // Prevents moving cursor to beginning/end of the text field in some browsers
                event.preventDefault();
            }
        },

        // Support: Chrome <=50
        // We should be able to just use this.element.prop( "isContentEditable" )
        // but hidden elements always report false in Chrome.
        // https://code.google.com/p/chromium/issues/detail?id=313082
        _isContentEditable: function( element ) {
            if ( !element.length ) {
                return false;
            }

            var editable = element.prop( "contentEditable" );

            if ( editable === "inherit" ) {
                return this._isContentEditable( element.parent() );
            }

            return editable === "true";
        }
    } );

    $.extend( $.ui.autocomplete, {
        escapeRegex: function( value ) {
            return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
        },
        filter: function( array, term ) {
            var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
            return $.grep( array, function( value ) {
                return matcher.test( value.label || value.value || value );
            } );
        }
    } );

// Live region extension, adding a `messages` option
// NOTE: This is an experimental API. We are still investigating
// a full solution for string manipulation and internationalization.
    $.widget( "ui.autocomplete", $.ui.autocomplete, {
        options: {
            messages: {
                noResults: "No search results.",
                results: function( amount ) {
                    return amount + ( amount > 1 ? " results are" : " result is" ) +
                        " available, use up and down arrow keys to navigate.";
                }
            }
        },

        __response: function( content ) {
            var message;
            this._superApply( arguments );
            if ( this.options.disabled || this.cancelSearch ) {
                return;
            }
            if ( content && content.length ) {
                message = this.options.messages.results( content.length );
            } else {
                message = this.options.messages.noResults;
            }
            clearTimeout( this.liveRegionTimer );
            this.liveRegionTimer = this._delay( function() {
                this.liveRegion.html( $( "<div>" ).text( message ) );
            }, 100 );
        }
    } );

    return $.ui.autocomplete;

} );

define('Amasty_GdprFrontendUi/js/mixins/ids-storage-mixin',[
    'jquery',
    'mage/utils/wrapper',
    'Amasty_GdprFrontendUi/js/model/cookie'
], function ($, wrapper, cookies) {
    'use strict';

    return function (idsStorage) {
        idsStorage.initLocalStorage = wrapper.wrapSuper(idsStorage.initLocalStorage, function () {
            var isCookieAllowed = true,
                cookieSetItem = window.cookieStorage.setItem,
                emptyFunction = function () {};

            isCookieAllowed = cookies.isCookieAllowed(this.namespace);

            if (isCookieAllowed || !window.isGdprCookieEnabled) {
                return this._super();
            }

            window.cookieStorage.setItem = emptyFunction;

            this._super();

            window.cookieStorage.setItem = cookieSetItem;

            return this;
        });

        return idsStorage;
    };
});

/**
 * 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/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();
        }
    };
});

define('WeltPixel_FrontendOptions/js/mute_migrate',[
    'jquery'
], function ($) {
    $.migrateMute = true;
    $.migrateTrace = false;
});

/*!
 * jQuery Migrate - v3.3.2 - 2020-11-17T23:22Z
 * Copyright OpenJS Foundation and other contributors
 */
( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/jquery-migrate',[ "jquery" ], function( jQuery ) {
            return factory( jQuery, window );
        } );
    } else if ( typeof module === "object" && module.exports ) {

        // Node/CommonJS
        // eslint-disable-next-line no-undef
        module.exports = factory( require( "jquery" ), window );
    } else {

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

    jQuery.migrateVersion = "3.3.2";

// Returns 0 if v1 == v2, -1 if v1 < v2, 1 if v1 > v2
    function compareVersions( v1, v2 ) {
        var i,
            rVersionParts = /^(\d+)\.(\d+)\.(\d+)/,
            v1p = rVersionParts.exec( v1 ) || [ ],
            v2p = rVersionParts.exec( v2 ) || [ ];

        for ( i = 1; i <= 3; i++ ) {
            if ( +v1p[ i ] > +v2p[ i ] ) {
                return 1;
            }
            if ( +v1p[ i ] < +v2p[ i ] ) {
                return -1;
            }
        }
        return 0;
    }

    function jQueryVersionSince( version ) {
        return compareVersions( jQuery.fn.jquery, version ) >= 0;
    }

    ( function() {

        // Support: IE9 only
        // IE9 only creates console object when dev tools are first opened
        // IE9 console is a host object, callable but doesn't have .apply()
        if ( !window.console || !window.console.log ) {
            return;
        }

        // Need jQuery 3.0.0+ and no older Migrate loaded
        if ( !jQuery || !jQueryVersionSince( "3.0.0" ) ) {
            window.console.log( "JQMIGRATE: jQuery 3.0.0+ REQUIRED" );
        }
        if ( jQuery.migrateWarnings ) {
            window.console.log( "JQMIGRATE: Migrate plugin loaded multiple times" );
        }

        // Show a message on the console so devs know we're active
        window.console.log( "JQMIGRATE: Migrate is installed" +
            ( jQuery.migrateMute ? "" : " with logging active" ) +
            ", version " + jQuery.migrateVersion );

    } )();

    var warnedAbout = {};

// By default each warning is only reported once.
    jQuery.migrateDeduplicateWarnings = true;

// List of warnings already given; public read only
    jQuery.migrateWarnings = [];

// Set to false to disable traces that appear with warnings
    if ( jQuery.migrateTrace === undefined ) {
        jQuery.migrateTrace = true;
    }

// Forget any warnings we've already given; public
    jQuery.migrateReset = function() {
        warnedAbout = {};
        jQuery.migrateWarnings.length = 0;
    };

    function migrateWarn( msg ) {
        var console = window.console;
        if ( !jQuery.migrateDeduplicateWarnings || !warnedAbout[ msg ] ) {
            warnedAbout[ msg ] = true;
            jQuery.migrateWarnings.push( msg );
            if ( console && console.warn && !jQuery.migrateMute ) {
                console.warn( "JQMIGRATE: " + msg );
                if ( jQuery.migrateTrace && console.trace ) {
                    console.trace();
                }
            }
        }
    }

    function migrateWarnProp( obj, prop, value, msg ) {
        Object.defineProperty( obj, prop, {
            configurable: true,
            enumerable: true,
            get: function() {
                migrateWarn( msg );
                return value;
            },
            set: function( newValue ) {
                migrateWarn( msg );
                value = newValue;
            }
        } );
    }

    function migrateWarnFunc( obj, prop, newFunc, msg ) {
        obj[ prop ] = function() {
            migrateWarn( msg );
            return newFunc.apply( this, arguments );
        };
    }

    if ( window.document.compatMode === "BackCompat" ) {

        // JQuery has never supported or tested Quirks Mode
        migrateWarn( "jQuery is not compatible with Quirks Mode" );
    }

    var findProp,
        class2type = {},
        oldInit = jQuery.fn.init,
        oldFind = jQuery.find,

        rattrHashTest = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/,
        rattrHashGlob = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/g,

        // Support: Android <=4.0 only
        // Make sure we trim BOM and NBSP
        rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

    jQuery.fn.init = function( arg1 ) {
        var args = Array.prototype.slice.call( arguments );

        if ( typeof arg1 === "string" && arg1 === "#" ) {

            // JQuery( "#" ) is a bogus ID selector, but it returned an empty set before jQuery 3.0
            migrateWarn( "jQuery( '#' ) is not a valid selector" );
            args[ 0 ] = [];
        }

        return oldInit.apply( this, args );
    };
    jQuery.fn.init.prototype = jQuery.fn;

    jQuery.find = function( selector ) {
        var args = Array.prototype.slice.call( arguments );

        // Support: PhantomJS 1.x
        // String#match fails to match when used with a //g RegExp, only on some strings
        if ( typeof selector === "string" && rattrHashTest.test( selector ) ) {

            // The nonstandard and undocumented unquoted-hash was removed in jQuery 1.12.0
            // First see if qS thinks it's a valid selector, if so avoid a false positive
            try {
                window.document.querySelector( selector );
            } catch ( err1 ) {

                // Didn't *look* valid to qSA, warn and try quoting what we think is the value
                selector = selector.replace( rattrHashGlob, function( _, attr, op, value ) {
                    return "[" + attr + op + "\"" + value + "\"]";
                } );

                // If the regexp *may* have created an invalid selector, don't update it
                // Note that there may be false alarms if selector uses jQuery extensions
                try {
                    window.document.querySelector( selector );
                    migrateWarn( "Attribute selector with '#' must be quoted: " + args[ 0 ] );
                    args[ 0 ] = selector;
                } catch ( err2 ) {
                    migrateWarn( "Attribute selector with '#' was not fixed: " + args[ 0 ] );
                }
            }
        }

        return oldFind.apply( this, args );
    };

// Copy properties attached to original jQuery.find method (e.g. .attr, .isXML)
    for ( findProp in oldFind ) {
        if ( Object.prototype.hasOwnProperty.call( oldFind, findProp ) ) {
            jQuery.find[ findProp ] = oldFind[ findProp ];
        }
    }

// The number of elements contained in the matched element set
    migrateWarnFunc( jQuery.fn, "size", function() {
            return this.length;
        },
        "jQuery.fn.size() is deprecated and removed; use the .length property" );

    migrateWarnFunc( jQuery, "parseJSON", function() {
            return JSON.parse.apply( null, arguments );
        },
        "jQuery.parseJSON is deprecated; use JSON.parse" );

    migrateWarnFunc( jQuery, "holdReady", jQuery.holdReady,
        "jQuery.holdReady is deprecated" );

    migrateWarnFunc( jQuery, "unique", jQuery.uniqueSort,
        "jQuery.unique is deprecated; use jQuery.uniqueSort" );

// Now jQuery.expr.pseudos is the standard incantation
    migrateWarnProp( jQuery.expr, "filters", jQuery.expr.pseudos,
        "jQuery.expr.filters is deprecated; use jQuery.expr.pseudos" );
    migrateWarnProp( jQuery.expr, ":", jQuery.expr.pseudos,
        "jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos" );

// Prior to jQuery 3.1.1 there were internal refs so we don't warn there
    if ( jQueryVersionSince( "3.1.1" ) ) {
        migrateWarnFunc( jQuery, "trim", function( text ) {
                return text == null ?
                    "" :
                    ( text + "" ).replace( rtrim, "" );
            },
            "jQuery.trim is deprecated; use String.prototype.trim" );
    }

// Prior to jQuery 3.2 there were internal refs so we don't warn there
    if ( jQueryVersionSince( "3.2.0" ) ) {
        migrateWarnFunc( jQuery, "nodeName", function( elem, name ) {
                return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
            },
            "jQuery.nodeName is deprecated" );

        migrateWarnFunc( jQuery, "isArray", Array.isArray,
            "jQuery.isArray is deprecated; use Array.isArray"
        );
    }

    if ( jQueryVersionSince( "3.3.0" ) ) {

        migrateWarnFunc( jQuery, "isNumeric", function( obj ) {

                // As of jQuery 3.0, isNumeric is limited to
                // strings and numbers (primitives or objects)
                // that can be coerced to finite numbers (gh-2662)
                var type = typeof obj;
                return ( type === "number" || type === "string" ) &&

                    // parseFloat NaNs numeric-cast false positives ("")
                    // ...but misinterprets leading-number strings, e.g. hex literals ("0x...")
                    // subtraction forces infinities to NaN
                    !isNaN( obj - parseFloat( obj ) );
            },
            "jQuery.isNumeric() is deprecated"
        );

        // Populate the class2type map
        jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".
            split( " " ),
            function( _, name ) {
                class2type[ "[object " + name + "]" ] = name.toLowerCase();
            } );

        migrateWarnFunc( jQuery, "type", function( obj ) {
                if ( obj == null ) {
                    return obj + "";
                }

                // Support: Android <=2.3 only (functionish RegExp)
                return typeof obj === "object" || typeof obj === "function" ?
                    class2type[ Object.prototype.toString.call( obj ) ] || "object" :
                    typeof obj;
            },
            "jQuery.type is deprecated" );

        migrateWarnFunc( jQuery, "isFunction",
            function( obj ) {
                return typeof obj === "function";
            },
            "jQuery.isFunction() is deprecated" );

        migrateWarnFunc( jQuery, "isWindow",
            function( obj ) {
                return obj != null && obj === obj.window;
            },
            "jQuery.isWindow() is deprecated"
        );
    }

// Support jQuery slim which excludes the ajax module
    if ( jQuery.ajax ) {

        var oldAjax = jQuery.ajax,
            rjsonp = /(=)\?(?=&|$)|\?\?/;

        jQuery.ajax = function( ) {
            var jQXHR = oldAjax.apply( this, arguments );

            // Be sure we got a jQXHR (e.g., not sync)
            if ( jQXHR.promise ) {
                migrateWarnFunc( jQXHR, "success", jQXHR.done,
                    "jQXHR.success is deprecated and removed" );
                migrateWarnFunc( jQXHR, "error", jQXHR.fail,
                    "jQXHR.error is deprecated and removed" );
                migrateWarnFunc( jQXHR, "complete", jQXHR.always,
                    "jQXHR.complete is deprecated and removed" );
            }

            return jQXHR;
        };

// Only trigger the logic in jQuery <4 as the JSON-to-JSONP auto-promotion
// behavior is gone in jQuery 4.0 and as it has security implications, we don't
// want to restore the legacy behavior.
        if ( !jQueryVersionSince( "4.0.0" ) ) {

            // Register this prefilter before the jQuery one. Otherwise, a promoted
            // request is transformed into one with the script dataType and we can't
            // catch it anymore.
            jQuery.ajaxPrefilter( "+json", function( s ) {

                // Warn if JSON-to-JSONP auto-promotion happens.
                if ( s.jsonp !== false && ( rjsonp.test( s.url ) ||
                    typeof s.data === "string" &&
                    ( s.contentType || "" )
                        .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
                    rjsonp.test( s.data )
                ) ) {
                    migrateWarn( "JSON-to-JSONP auto-promotion is deprecated" );
                }
            } );
        }

    }

    var oldRemoveAttr = jQuery.fn.removeAttr,
        oldToggleClass = jQuery.fn.toggleClass,
        rmatchNonSpace = /\S+/g;

    jQuery.fn.removeAttr = function( name ) {
        var self = this;

        jQuery.each( name.match( rmatchNonSpace ), function( _i, attr ) {
            if ( jQuery.expr.match.bool.test( attr ) ) {
                migrateWarn( "jQuery.fn.removeAttr no longer sets boolean properties: " + attr );
                self.prop( attr, false );
            }
        } );

        return oldRemoveAttr.apply( this, arguments );
    };

    jQuery.fn.toggleClass = function( state ) {

        // Only deprecating no-args or single boolean arg
        if ( state !== undefined && typeof state !== "boolean" ) {
            return oldToggleClass.apply( this, arguments );
        }

        migrateWarn( "jQuery.fn.toggleClass( boolean ) is deprecated" );

        // Toggle entire class name of each element
        return this.each( function() {
            var className = this.getAttribute && this.getAttribute( "class" ) || "";

            if ( className ) {
                jQuery.data( this, "__className__", className );
            }

            // If the element has a class name or if we're passed `false`,
            // then remove the whole classname (if there was one, the above saved it).
            // Otherwise bring back whatever was previously saved (if anything),
            // falling back to the empty string if nothing was stored.
            if ( this.setAttribute ) {
                this.setAttribute( "class",
                    className || state === false ?
                        "" :
                        jQuery.data( this, "__className__" ) || ""
                );
            }
        } );
    };

    function camelCase( string ) {
        return string.replace( /-([a-z])/g, function( _, letter ) {
            return letter.toUpperCase();
        } );
    }

    var oldFnCss,
        internalSwapCall = false,
        ralphaStart = /^[a-z]/,

        // The regex visualized:
        //
        //                         /----------\
        //                        |            |    /-------\
        //                        |  / Top  \  |   |         |
        //         /--- Border ---+-| Right  |-+---+- Width -+---\
        //        |                 | Bottom |                    |
        //        |                  \ Left /                     |
        //        |                                               |
        //        |                              /----------\     |
        //        |          /-------------\    |            |    |- END
        //        |         |               |   |  / Top  \  |    |
        //        |         |  / Margin  \  |   | | Right  | |    |
        //        |---------+-|           |-+---+-| Bottom |-+----|
        //        |            \ Padding /         \ Left /       |
        // BEGIN -|                                               |
        //        |                /---------\                    |
        //        |               |           |                   |
        //        |               |  / Min \  |    / Width  \     |
        //         \--------------+-|       |-+---|          |---/
        //                           \ Max /       \ Height /
        rautoPx = /^(?:Border(?:Top|Right|Bottom|Left)?(?:Width|)|(?:Margin|Padding)?(?:Top|Right|Bottom|Left)?|(?:Min|Max)?(?:Width|Height))$/;

// If this version of jQuery has .swap(), don't false-alarm on internal uses
    if ( jQuery.swap ) {
        jQuery.each( [ "height", "width", "reliableMarginRight" ], function( _, name ) {
            var oldHook = jQuery.cssHooks[ name ] && jQuery.cssHooks[ name ].get;

            if ( oldHook ) {
                jQuery.cssHooks[ name ].get = function() {
                    var ret;

                    internalSwapCall = true;
                    ret = oldHook.apply( this, arguments );
                    internalSwapCall = false;
                    return ret;
                };
            }
        } );
    }

    jQuery.swap = function( elem, options, callback, args ) {
        var ret, name,
            old = {};

        if ( !internalSwapCall ) {
            migrateWarn( "jQuery.swap() is undocumented and deprecated" );
        }

        // Remember the old values, and insert the new ones
        for ( name in options ) {
            old[ name ] = elem.style[ name ];
            elem.style[ name ] = options[ name ];
        }

        ret = callback.apply( elem, args || [] );

        // Revert the old values
        for ( name in options ) {
            elem.style[ name ] = old[ name ];
        }

        return ret;
    };

    if ( jQueryVersionSince( "3.4.0" ) && typeof Proxy !== "undefined" ) {

        jQuery.cssProps = new Proxy( jQuery.cssProps || {}, {
            set: function() {
                migrateWarn( "JQMIGRATE: jQuery.cssProps is deprecated" );
                return Reflect.set.apply( this, arguments );
            }
        } );
    }

// Create a dummy jQuery.cssNumber if missing. It won't be used by jQuery but
// it will prevent code adding new keys to it unconditionally from crashing.
    if ( !jQuery.cssNumber ) {
        jQuery.cssNumber = {};
    }

    function isAutoPx( prop ) {

        // The first test is used to ensure that:
        // 1. The prop starts with a lowercase letter (as we uppercase it for the second regex).
        // 2. The prop is not empty.
        return ralphaStart.test( prop ) &&
            rautoPx.test( prop[ 0 ].toUpperCase() + prop.slice( 1 ) );
    }

    oldFnCss = jQuery.fn.css;

    jQuery.fn.css = function( name, value ) {
        var camelName,
            origThis = this;
        if ( name && typeof name === "object" && !Array.isArray( name ) ) {
            jQuery.each( name, function( n, v ) {
                jQuery.fn.css.call( origThis, n, v );
            } );
            return this;
        }
        if ( typeof value === "number" ) {
            camelName = camelCase( name );
            if ( !isAutoPx( camelName ) && !jQuery.cssNumber[ camelName ] ) {
                migrateWarn( "Number-typed values are deprecated for jQuery.fn.css( \"" +
                    name + "\", value )" );
            }
        }

        return oldFnCss.apply( this, arguments );
    };

    var oldData = jQuery.data;

    jQuery.data = function( elem, name, value ) {
        var curData, sameKeys, key;

        // Name can be an object, and each entry in the object is meant to be set as data
        if ( name && typeof name === "object" && arguments.length === 2 ) {
            curData = jQuery.hasData( elem ) && oldData.call( this, elem );
            sameKeys = {};
            for ( key in name ) {
                if ( key !== camelCase( key ) ) {
                    migrateWarn( "jQuery.data() always sets/gets camelCased names: " + key );
                    curData[ key ] = name[ key ];
                } else {
                    sameKeys[ key ] = name[ key ];
                }
            }

            oldData.call( this, elem, sameKeys );

            return name;
        }

        // If the name is transformed, look for the un-transformed name in the data object
        if ( name && typeof name === "string" && name !== camelCase( name ) ) {
            curData = jQuery.hasData( elem ) && oldData.call( this, elem );
            if ( curData && name in curData ) {
                migrateWarn( "jQuery.data() always sets/gets camelCased names: " + name );
                if ( arguments.length > 2 ) {
                    curData[ name ] = value;
                }
                return curData[ name ];
            }
        }

        return oldData.apply( this, arguments );
    };

// Support jQuery slim which excludes the effects module
    if ( jQuery.fx ) {

        var intervalValue, intervalMsg,
            oldTweenRun = jQuery.Tween.prototype.run,
            linearEasing = function( pct ) {
                return pct;
            };

        jQuery.Tween.prototype.run = function( ) {
            if ( jQuery.easing[ this.easing ].length > 1 ) {
                migrateWarn(
                    "'jQuery.easing." + this.easing.toString() + "' should use only one argument"
                );

                jQuery.easing[ this.easing ] = linearEasing;
            }

            oldTweenRun.apply( this, arguments );
        };

        intervalValue = jQuery.fx.interval || 13;
        intervalMsg = "jQuery.fx.interval is deprecated";

// Support: IE9, Android <=4.4
// Avoid false positives on browsers that lack rAF
// Don't warn if document is hidden, jQuery uses setTimeout (#292)
        if ( window.requestAnimationFrame ) {
            Object.defineProperty( jQuery.fx, "interval", {
                configurable: true,
                enumerable: true,
                get: function() {
                    if ( !window.document.hidden ) {
                        migrateWarn( intervalMsg );
                    }
                    return intervalValue;
                },
                set: function( newValue ) {
                    migrateWarn( intervalMsg );
                    intervalValue = newValue;
                }
            } );
        }

    }

    var oldLoad = jQuery.fn.load,
        oldEventAdd = jQuery.event.add,
        originalFix = jQuery.event.fix;

    jQuery.event.props = [];
    jQuery.event.fixHooks = {};

    migrateWarnProp( jQuery.event.props, "concat", jQuery.event.props.concat,
        "jQuery.event.props.concat() is deprecated and removed" );

    jQuery.event.fix = function( originalEvent ) {
        var event,
            type = originalEvent.type,
            fixHook = this.fixHooks[ type ],
            props = jQuery.event.props;

        if ( props.length ) {
            migrateWarn( "jQuery.event.props are deprecated and removed: " + props.join() );
            while ( props.length ) {
                jQuery.event.addProp( props.pop() );
            }
        }

        if ( fixHook && !fixHook._migrated_ ) {
            fixHook._migrated_ = true;
            migrateWarn( "jQuery.event.fixHooks are deprecated and removed: " + type );
            if ( ( props = fixHook.props ) && props.length ) {
                while ( props.length ) {
                    jQuery.event.addProp( props.pop() );
                }
            }
        }

        event = originalFix.call( this, originalEvent );

        return fixHook && fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
    };

    jQuery.event.add = function( elem, types ) {

        // This misses the multiple-types case but that seems awfully rare
        if ( elem === window && types === "load" && window.document.readyState === "complete" ) {
            migrateWarn( "jQuery(window).on('load'...) called after load event occurred" );
        }
        return oldEventAdd.apply( this, arguments );
    };

    jQuery.each( [ "load", "unload", "error" ], function( _, name ) {

        jQuery.fn[ name ] = function() {
            var args = Array.prototype.slice.call( arguments, 0 );

            // If this is an ajax load() the first arg should be the string URL;
            // technically this could also be the "Anything" arg of the event .load()
            // which just goes to show why this dumb signature has been deprecated!
            // jQuery custom builds that exclude the Ajax module justifiably die here.
            if ( name === "load" && typeof args[ 0 ] === "string" ) {
                return oldLoad.apply( this, args );
            }

            migrateWarn( "jQuery.fn." + name + "() is deprecated" );

            args.splice( 0, 0, name );
            if ( arguments.length ) {
                return this.on.apply( this, args );
            }

            // Use .triggerHandler here because:
            // - load and unload events don't need to bubble, only applied to window or image
            // - error event should not bubble to window, although it does pre-1.7
            // See http://bugs.jquery.com/ticket/11820
            this.triggerHandler.apply( this, args );
            return this;
        };

    } );

    jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
            "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
            "change select submit keydown keypress keyup contextmenu" ).split( " " ),
        function( _i, name ) {

            // Handle event binding
            jQuery.fn[ name ] = function( data, fn ) {
                migrateWarn( "jQuery.fn." + name + "() event shorthand is deprecated" );
                return arguments.length > 0 ?
                    this.on( name, null, data, fn ) :
                    this.trigger( name );
            };
        } );

// Trigger "ready" event only once, on document ready
    jQuery( function() {
        jQuery( window.document ).triggerHandler( "ready" );
    } );

    jQuery.event.special.ready = {
        setup: function() {
            if ( this === window.document ) {
                migrateWarn( "'ready' event is deprecated" );
            }
        }
    };

    jQuery.fn.extend( {

        bind: function( types, data, fn ) {
            migrateWarn( "jQuery.fn.bind() is deprecated" );
            return this.on( types, null, data, fn );
        },
        unbind: function( types, fn ) {
            migrateWarn( "jQuery.fn.unbind() is deprecated" );
            return this.off( types, null, fn );
        },
        delegate: function( selector, types, data, fn ) {
            migrateWarn( "jQuery.fn.delegate() is deprecated" );
            return this.on( types, selector, data, fn );
        },
        undelegate: function( selector, types, fn ) {
            migrateWarn( "jQuery.fn.undelegate() is deprecated" );
            return arguments.length === 1 ?
                this.off( selector, "**" ) :
                this.off( types, selector || "**", fn );
        },
        hover: function( fnOver, fnOut ) {
            migrateWarn( "jQuery.fn.hover() is deprecated" );
            return this.on( "mouseenter", fnOver ).on( "mouseleave", fnOut || fnOver );
        }
    } );

    var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
        origHtmlPrefilter = jQuery.htmlPrefilter,
        makeMarkup = function( html ) {
            var doc = window.document.implementation.createHTMLDocument( "" );
            doc.body.innerHTML = html;
            return doc.body && doc.body.innerHTML;
        },
        warnIfChanged = function( html ) {
            var changed = html.replace( rxhtmlTag, "<$1></$2>" );
            if ( changed !== html && makeMarkup( html ) !== makeMarkup( changed ) ) {
                migrateWarn( "HTML tags must be properly nested and closed: " + html );
            }
        };

    jQuery.UNSAFE_restoreLegacyHtmlPrefilter = function() {
        jQuery.htmlPrefilter = function( html ) {
            warnIfChanged( html );
            return html.replace( rxhtmlTag, "<$1></$2>" );
        };
    };

    jQuery.htmlPrefilter = function( html ) {
        warnIfChanged( html );
        return origHtmlPrefilter( html );
    };

    var oldOffset = jQuery.fn.offset;

    jQuery.fn.offset = function() {
        var elem = this[ 0 ];

        if ( elem && ( !elem.nodeType || !elem.getBoundingClientRect ) ) {
            migrateWarn( "jQuery.fn.offset() requires a valid DOM element" );
            return arguments.length ? this : undefined;
        }

        return oldOffset.apply( this, arguments );
    };

// Support jQuery slim which excludes the ajax module
// The jQuery.param patch is about respecting `jQuery.ajaxSettings.traditional`
// so it doesn't make sense for the slim build.
    if ( jQuery.ajax ) {

        var oldParam = jQuery.param;

        jQuery.param = function( data, traditional ) {
            var ajaxTraditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;

            if ( traditional === undefined && ajaxTraditional ) {

                migrateWarn( "jQuery.param() no longer uses jQuery.ajaxSettings.traditional" );
                traditional = ajaxTraditional;
            }

            return oldParam.call( this, data, traditional );
        };

    }

    var oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack;

    jQuery.fn.andSelf = function() {
        migrateWarn( "jQuery.fn.andSelf() is deprecated and removed, use jQuery.fn.addBack()" );
        return oldSelf.apply( this, arguments );
    };

// Support jQuery slim which excludes the deferred module in jQuery 4.0+
    if ( jQuery.Deferred ) {

        var oldDeferred = jQuery.Deferred,
            tuples = [

                // Action, add listener, callbacks, .then handlers, final state
                [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks( "memory" ),
                    jQuery.Callbacks( "memory" ) ]
            ];

        jQuery.Deferred = function( func ) {
            var deferred = oldDeferred(),
                promise = deferred.promise();

            deferred.pipe = promise.pipe = function( /* fnDone, fnFail, fnProgress */ ) {
                var fns = arguments;

                migrateWarn( "deferred.pipe() is deprecated" );

                return jQuery.Deferred( function( newDefer ) {
                    jQuery.each( tuples, function( i, tuple ) {
                        var fn = typeof fns[ i ] === "function" && fns[ i ];

                        // Deferred.done(function() { bind to newDefer or newDefer.resolve })
                        // deferred.fail(function() { bind to newDefer or newDefer.reject })
                        // deferred.progress(function() { bind to newDefer or newDefer.notify })
                        deferred[ tuple[ 1 ] ]( function() {
                            var returned = fn && fn.apply( this, arguments );
                            if ( returned && typeof returned.promise === "function" ) {
                                returned.promise()
                                    .done( newDefer.resolve )
                                    .fail( newDefer.reject )
                                    .progress( newDefer.notify );
                            } else {
                                newDefer[ tuple[ 0 ] + "With" ](
                                    this === promise ? newDefer.promise() : this,
                                    fn ? [ returned ] : arguments
                                );
                            }
                        } );
                    } );
                    fns = null;
                } ).promise();

            };

            if ( func ) {
                func.call( deferred, deferred );
            }

            return deferred;
        };

// Preserve handler of uncaught exceptions in promise chains
        jQuery.Deferred.exceptionHook = oldDeferred.exceptionHook;

    }

    return jQuery;
} );

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

//>>label: Shake Effect
//>>group: Effects
//>>description: Shakes an element horizontally or vertically n times.
//>>docs: http://api.jqueryui.com/shake-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "shake", function( options, done ) {

        var i = 1,
            element = $( this ),
            direction = options.direction || "left",
            distance = options.distance || 20,
            times = options.times || 3,
            anims = times * 2 + 1,
            speed = Math.round( options.duration / anims ),
            ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
            positiveMotion = ( direction === "up" || direction === "left" ),
            animation = {},
            animation1 = {},
            animation2 = {},

            queuelen = element.queue().length;

        $.effects.createPlaceholder( element );

        // Animation
        animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
        animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
        animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;

        // Animate
        element.animate( animation, speed, options.easing );

        // Shakes
        for ( ; i < times; i++ ) {
            element
                .animate( animation1, speed, options.easing )
                .animate( animation2, speed, options.easing );
        }

        element
            .animate( animation1, speed, options.easing )
            .animate( animation, speed / 2, options.easing )
            .queue( done );

        $.effects.unshift( element, queuelen, anims + 1 );
    } );

} );

/**
 * jQuery Unveil
 * A very lightweight jQuery plugin to lazy load images
 * http://luis-almeida.github.com/unveil
 *
 * Licensed under the MIT license.
 * Copyright 2013 Luís Almeida
 * https://github.com/luis-almeida
 */

define('Magefan_Blog/js/lib/mfblogunveil',['jquery'], function($) {
    /* Origin https://github.com/luis-almeida/unveil/blob/master/jquery.unveil.js */
    $.fn.mfblogunveil = function(threshold, callback) {

        var $w = $(window),
            th = threshold || 0,
            attrib = 'data-original',
            images = this,
            loaded;

        this.one("mfblogunveil", function() {
            var source = this.getAttribute(attrib);
            /*source = source || this.getAttribute("data-src");*/
            if (source) {
                /*this.setAttribute("src", source);*/
                var style = this.getAttribute('style') ? (this.getAttribute('style') + '; ') : '';
                style = style + 'background-image: url("' + source + '");'
                this.setAttribute('style', style);

                if (typeof callback === "function") callback.call(this);
            }
        });

        function mfblogunveil() {
            var inview = images.filter(function() {
                var $e = $(this);
                if ($e.is(":hidden")) return;

                var wt = $w.scrollTop(),
                    wb = wt + $w.height(),
                    et = $e.offset().top,
                    eb = et + $e.height();

                return eb >= wt - th && et <= wb + th;
            });

            loaded = inview.trigger("mfblogunveil");
            images = images.not(loaded);
        }

        $w.on("scroll.mfblogunveil resize.mfblogunveil lookup.mfblogunveil", mfblogunveil);

        mfblogunveil();

        return this;
    };
});

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('mixins', [
    'module'
], function (module) {
    'use strict';

    var contexts = require.s.contexts,
        defContextName = '_',
        defContext = contexts[defContextName],
        unbundledContext = require.s.newContext('$'),
        defaultConfig = defContext.config,
        unbundledConfig = {
            baseUrl: defaultConfig.baseUrl,
            paths: defaultConfig.paths,
            shim: defaultConfig.shim,
            config: defaultConfig.config,
            map: defaultConfig.map
        },
        rjsMixins;

    /**
     * Prepare a separate context where modules are not assigned to bundles
     * so we are able to get their true path and corresponding mixins.
     */
    unbundledContext.configure(unbundledConfig);

    /**
     * Checks if specified string contains
     * a plugin spacer '!' substring.
     *
     * @param {String} name - Name, path or alias of a module.
     * @returns {Boolean}
     */
    function hasPlugin(name) {
        return !!~name.indexOf('!');
    }

    /**
     * Adds 'mixins!' prefix to the specified string.
     *
     * @param {String} name - Name, path or alias of a module.
     * @returns {String} Modified name.
     */
    function addPlugin(name) {
        return 'mixins!' + name;
    }

    /**
     * Removes base url from the provided string.
     *
     * @param {String} url - Url to be processed.
     * @param {Object} config - Contexts' configuration object.
     * @returns {String} String without base url.
     */
    function removeBaseUrl(url, config) {
        var baseUrl = config.baseUrl || '',
            index = url.indexOf(baseUrl);

        if (~index) {
            url = url.substring(baseUrl.length - index);
        }

        return url;
    }

    /**
     * Extracts url (without baseUrl prefix)
     * from a module name ignoring the fact that it may be bundled.
     *
     * @param {String} name - Name, path or alias of a module.
     * @param {Object} config - Context's configuration.
     * @returns {String}
     */
    function getPath(name, config) {
        var url = unbundledContext.require.toUrl(name);

        return removeBaseUrl(url, config);
    }

    /**
     * Checks if specified string represents a relative path (../).
     *
     * @param {String} name - Name, path or alias of a module.
     * @returns {Boolean}
     */
    function isRelative(name) {
        return !!~name.indexOf('./');
    }

    /**
     * Iteratively calls mixins passing to them
     * current value of a 'target' parameter.
     *
     * @param {*} target - Value to be modified.
     * @param {...Function} mixins - List of mixins to apply.
     * @returns {*} Modified 'target' value.
     */
    function applyMixins(target) {
        var mixins = Array.prototype.slice.call(arguments, 1);

        mixins.forEach(function (mixin) {
            target = mixin(target);
        });

        return target;
    }

    rjsMixins = {

        /**
         * Loads specified module along with its' mixins.
         * This method is called for each module defined with "mixins!" prefix
         * in its name that was added by processNames method.
         *
         * @param {String} name - Module to be loaded.
         * @param {Function} req - Local "require" function to use to load other modules.
         * @param {Function} onLoad - A function to call with the value for name.
         * @param {Object} config - RequireJS configuration object.
         */
        load: function (name, req, onLoad, config) {
            var path     = getPath(name, config),
                mixins   = this.getMixins(path),
                deps     = [name].concat(mixins);

            req(deps, function () {
                onLoad(applyMixins.apply(null, arguments));
            });
        },

        /**
         * Retrieves list of mixins associated with a specified module.
         *
         * @param {String} path - Path to the module (without base URL).
         * @returns {Array} An array of paths to mixins.
         */
        getMixins: function (path) {
            var config = module.config() || {},
                mixins;

            // Fix for when urlArgs is set.
            if (path.indexOf('?') !== -1) {
                path = path.substring(0, path.indexOf('?'));
            }
            mixins = config[path] || {};

            return Object.keys(mixins).filter(function (mixin) {
                return mixins[mixin] !== false;
            });
        },

        /**
         * Checks if specified module has associated with it mixins.
         *
         * @param {String} path - Path to the module (without base URL).
         * @returns {Boolean}
         */
        hasMixins: function (path) {
            return this.getMixins(path).length;
        },

        /**
         * Modifies provided names prepending to them
         * the 'mixins!' plugin prefix if it's necessary.
         *
         * @param {(Array|String)} names - Module names, paths or aliases.
         * @param {Object} context - Current RequireJS context.
         * @returns {Array|String}
         */
        processNames: function (names, context) {
            var config = context.config;

            /**
             * Prepends 'mixin' plugin to a single name.
             *
             * @param {String} name
             * @returns {String}
             */
            function processName(name) {
                var path = getPath(name, config);

                if (!hasPlugin(name) && (isRelative(name) || rjsMixins.hasMixins(path))) {
                    return addPlugin(name);
                }

                return name;
            }

            return typeof names !== 'string' ?
                names.map(processName) :
                processName(names);
        }
    };

    return rjsMixins;
});

require([
    'mixins'
], function (mixins) {
    'use strict';

    var contexts = require.s.contexts,
        defContextName = '_',
        defContext = contexts[defContextName],
        originalContextRequire = defContext.require,
        processNames = mixins.processNames;

    /**
     * Wrap default context's require function which gets called every time
     * module is requested using require call. The upside of this approach
     * is that deps parameter is already normalized and guaranteed to be an array.
     */
    defContext.require = function (deps, callback, errback) {
        deps = processNames(deps, defContext);

        return originalContextRequire(deps, callback, errback);
    };

    /**
     * Copy properties of original 'require' method.
     */
    Object.keys(originalContextRequire).forEach(function (key) {
        defContext.require[key] = originalContextRequire[key];
    });

    /**
     * Wrap shift method from context's definitions queue.
     * Items are added to the queue when a new module is defined and taken
     * from it every time require call happens.
     */
    defContext.defQueue.shift = function () {
        var queueItem = Array.prototype.shift.call(this),
            lastDeps = queueItem && queueItem[1];

        if (Array.isArray(lastDeps)) {
            queueItem[1] = processNames(queueItem[1], defContext);
        }

        return queueItem;
    };
});

define("mage/requirejs/mixins", function(){});

/**
 * Owl carousel
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 * @todo Lazy Load Icon
 * @todo prevent animationend bubling
 * @todo itemsScaleUp
 * @todo Test Zepto
 * @todo stagePadding calculate wrong active classes
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module depending on jQuery.
        define('WeltPixel_OwlCarouselSlider/js/owl.carousel',['jquery'], factory);
    } else {
        // No AMD. Register plugin with global jQuery object.
        factory(jQuery);
    }
}(function ($) {
    "use strict";
    var drag, state, e;

    /**
     * Template for status information about drag and touch events.
     * @private
     */
    drag = {
        start: 0,
        startX: 0,
        startY: 0,
        current: 0,
        currentX: 0,
        currentY: 0,
        offsetX: 0,
        offsetY: 0,
        distance: null,
        startTime: 0,
        endTime: 0,
        updatedX: 0,
        targetEl: null
    };

    /**
     * Template for some status informations.
     * @private
     */
    state = {
        isTouch: false,
        isScrolling: false,
        isSwiping: false,
        direction: false,
        inMotion: false
    };

    /**
     * Event functions references.
     * @private
     */
    e = {
        _onDragStart: null,
        _onDragMove: null,
        _onDragEnd: null,
        _transitionEnd: null,
        _resizer: null,
        _responsiveCall: null,
        _goToLoop: null,
        _checkVisibile: null
    };

    /**
     * Creates a carousel.
     * @class The Owl Carousel.
     * @public
     * @param {HTMLElement|jQuery} element - The element to create the carousel for.
     * @param {Object} [options] - The options
     */
    function Owl(element, options) {

        /**
         * Current settings for the carousel.
         * @public
         */
        this.settings = null;

        /**
         * Current options set by the caller including defaults.
         * @public
         */
        this.options = $.extend({}, Owl.Defaults, options);

        /**
         * Plugin element.
         * @public
         */
        this.$element = $(element);

        /**
         * Caches informations about drag and touch events.
         */
        this.drag = $.extend({}, drag);

        /**
         * Caches some status informations.
         * @protected
         */
        this.state = $.extend({}, state);

        /**
         * @protected
         * @todo Must be documented
         */
        this.e = $.extend({}, e);

        /**
         * References to the running plugins of this carousel.
         * @protected
         */
        this._plugins = {};

        /**
         * Currently suppressed events to prevent them from beeing retriggered.
         * @protected
         */
        this._supress = {};

        /**
         * Absolute current position.
         * @protected
         */
        this._current = null;

        /**
         * Animation speed in milliseconds.
         * @protected
         */
        this._speed = null;

        /**
         * Coordinates of all items in pixel.
         * @todo The name of this member is missleading.
         * @protected
         */
        this._coordinates = [];

        /**
         * Current breakpoint.
         * @todo Real media queries would be nice.
         * @protected
         */
        this._breakpoint = null;

        /**
         * Current width of the plugin element.
         */
        this._width = null;

        /**
         * All real items.
         * @protected
         */
        this._items = [];

        /**
         * All cloned items.
         * @protected
         */
        this._clones = [];

        /**
         * Merge values of all items.
         * @todo Maybe this could be part of a plugin.
         * @protected
         */
        this._mergers = [];

        /**
         * Invalidated parts within the update process.
         * @protected
         */
        this._invalidated = {};

        /**
         * Ordered list of workers for the update process.
         * @protected
         */
        this._pipe = [];

        $.each(Owl.Plugins, $.proxy(function(key, plugin) {
            this._plugins[key[0].toLowerCase() + key.slice(1)]
                = new plugin(this);
        }, this));

        $.each(Owl.Pipe, $.proxy(function(priority, worker) {
            this._pipe.push({
                'filter': worker.filter,
                'run': $.proxy(worker.run, this)
            });
        }, this));

        this.setup();
        this.initialize();
    }

    /**
     * Default options for the carousel.
     * @public
     */
    Owl.Defaults = {
        items: 3,
        loop: false,
        center: false,

        mouseDrag: true,
        touchDrag: true,
        pullDrag: true,
        freeDrag: false,

        margin: 0,
        stagePadding: 0,

        merge: false,
        mergeFit: true,
        autoWidth: false,

        startPosition: 0,
        rtl: false,

        smartSpeed: 250,
        fluidSpeed: false,
        dragEndSpeed: false,

        responsive: {},
        responsiveRefreshRate: 200,
        responsiveBaseElement: window,
        responsiveClass: false,

        fallbackEasing: 'swing',

        info: false,

        nestedItemSelector: false,
        itemElement: 'div',
        stageElement: 'div',

        // Classes and Names
        themeClass: 'owl-theme',
        baseClass: 'owl-carousel',
        itemClass: 'owl-item',
        centerClass: 'center',
        activeClass: 'active'
    };

    /**
     * Enumeration for width.
     * @public
     * @readonly
     * @enum {String}
     */
    Owl.Width = {
        Default: 'default',
        Inner: 'inner',
        Outer: 'outer'
    };

    /**
     * Contains all registered plugins.
     * @public
     */
    Owl.Plugins = {};

    /**
     * Update pipe.
     */
    Owl.Pipe = [ {
        filter: [ 'width', 'items', 'settings' ],
        run: function(cache) {
            cache.current = this._items && this._items[this.relative(this._current)];
        }
    }, {
        filter: [ 'items', 'settings' ],
        run: function() {
            var cached = this._clones,
                clones = this.$stage.children('.cloned');

            if (clones.length !== cached.length || (!this.settings.loop && cached.length > 0)) {
                this.$stage.children('.cloned').remove();
                this._clones = [];
            }
        }
    }, {
        filter: [ 'items', 'settings' ],
        run: function() {
            var i, n,
                clones = this._clones,
                items = this._items,
                // delta = this.settings.loop ? clones.length - Math.max(this.settings.items * 2, 4) : 0;
                delta = this.settings.loop ? clones.length - Math.max(this.settings.items * 2, 3) : 0;

            // for (i = 0, n = Math.abs(delta / 2); i < n; i++) {
            for (i = 0, n = Math.floor(Math.abs(delta / 2)); i < n; i++) {
                if (delta > 0) {
                    this.$stage.children().eq(items.length + clones.length - 1).remove();
                    clones.pop();
                    this.$stage.children().eq(0).remove();
                    clones.pop();
                } else {
                    clones.push(clones.length / 2);
                    this.$stage.append(items[clones[clones.length - 1]].clone().addClass('cloned'));
                    clones.push(items.length - 1 - (clones.length - 1) / 2);
                    this.$stage.prepend(items[clones[clones.length - 1]].clone().addClass('cloned'));
                }
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run: function() {
            var rtl = (this.settings.rtl ? 1 : -1),
                width = (this.width() / this.settings.items).toFixed(3),
                coordinate = 0, merge, i, n;

            this._coordinates = [];
            for (i = 0, n = this._clones.length + this._items.length; i < n; i++) {
                merge = this._mergers[this.relative(i)];
                merge = (this.settings.mergeFit && Math.min(merge, this.settings.items)) || merge;
                coordinate += (this.settings.autoWidth ? this._items[this.relative(i)].width() + this.settings.margin : width * merge) * rtl;

                this._coordinates.push(coordinate);
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run: function() {
            var i, n, width = (this.width() / this.settings.items).toFixed(3), css = {
                'width': Math.abs(this._coordinates[this._coordinates.length - 1]) + this.settings.stagePadding * 2,
                'padding-left': this.settings.stagePadding || '',
                'padding-right': this.settings.stagePadding || ''
            };

            this.$stage.css(css);

            css = { 'width': this.settings.autoWidth ? 'auto' : width - this.settings.margin };
            css[this.settings.rtl ? 'margin-left' : 'margin-right'] = this.settings.margin;

            if (!this.settings.autoWidth && $.grep(this._mergers, function(v) { return v > 1 }).length > 0) {
                for (i = 0, n = this._coordinates.length; i < n; i++) {
                    css.width = Math.abs(this._coordinates[i]) - Math.abs(this._coordinates[i - 1] || 0) - this.settings.margin;
                    this.$stage.children().eq(i).css(css);
                }
            } else {
                this.$stage.children().css(css);
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run: function(cache) {
            cache.current && this.reset(this.$stage.children().index(cache.current));
        }
    }, {
        filter: [ 'position' ],
        run: function() {
            this.animate(this.coordinates(this._current));
        }
    }, {
        filter: [ 'width', 'position', 'items', 'settings' ],
        run: function() {
            var rtl = this.settings.rtl ? 1 : -1,
                padding = this.settings.stagePadding * 2,
                begin = this.coordinates(this.current()) + padding,
                end = begin + this.width() * rtl,
                inner, outer, matches = [], i, n;

            for (i = 0, n = this._coordinates.length; i < n; i++) {
                inner = this._coordinates[i - 1] || 0;
                outer = Math.abs(this._coordinates[i]) + padding * rtl;

                if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
                    || (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
                    matches.push(i);
                }
            }

            this.$stage.children('.' + this.settings.activeClass).removeClass(this.settings.activeClass);
            this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass(this.settings.activeClass);

            if (this.settings.center) {
                this.$stage.children('.' + this.settings.centerClass).removeClass(this.settings.centerClass);
                this.$stage.children().eq(this.current()).addClass(this.settings.centerClass);
            }
        }
    } ];

    /**
     * Initializes the carousel.
     * @protected
     */
    Owl.prototype.initialize = function() {
        this.trigger('initialize');

        this.$element
            .addClass(this.settings.baseClass)
            .addClass(this.settings.themeClass)
            .toggleClass('owl-rtl', this.settings.rtl);

        // check support
        this.browserSupport();

        if (this.settings.autoWidth && this.state.imagesLoaded !== true) {
            var imgs, nestedSelector, width;
            imgs = this.$element.find('img');
            nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
            width = this.$element.children(nestedSelector).width();

            if (imgs.length && width <= 0) {
                this.preloadAutoWidthImages(imgs);
                return false;
            }
        }

        this.$element.addClass('owl-loading');

        // create stage
        this.$stage = $('<' + this.settings.stageElement + ' class="owl-stage"/>')
            .wrap('<div class="owl-stage-outer">');

        // append stage
        this.$element.append(this.$stage.parent());

        // append content
        this.replace(this.$element.children().not(this.$stage.parent()));

        // set view width
        this._width = this.$element.width();

        // update view
        this.refresh();

        this.$element.removeClass('owl-loading').addClass('owl-loaded');

        // attach generic events
        this.eventsCall();

        // attach generic events
        this.internalEvents();

        // attach custom control events
        this.addTriggerableEvents();

        this.trigger('initialized');
    };

    /**
     * Setups the current settings.
     * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
     * @todo Support for media queries by using `matchMedia` would be nice.
     * @public
     */
    Owl.prototype.setup = function() {
        var viewport = this.viewport(),
            overwrites = this.options.responsive,
            match = -1,
            settings = null;

        if (!overwrites) {
            settings = $.extend({}, this.options);
        } else {
            $.each(overwrites, function(breakpoint) {
                if (breakpoint <= viewport && breakpoint > match) {
                    match = Number(breakpoint);
                }
            });

            settings = $.extend({}, this.options, overwrites[match]);
            delete settings.responsive;

            // responsive class
            if (settings.responsiveClass) {
                this.$element.attr('class', function(i, c) {
                    return c.replace(/\b owl-responsive-\S+/g, '');
                }).addClass('owl-responsive-' + match);
            }
        }

        if (this.settings === null || this._breakpoint !== match) {
            this.trigger('change', { property: { name: 'settings', value: settings } });
            this._breakpoint = match;
            this.settings = settings;
            this.invalidate('settings');
            this.trigger('changed', { property: { name: 'settings', value: this.settings } });
        }
    };

    /**
     * Updates option logic if necessery.
     * @protected
     */
    Owl.prototype.optionsLogic = function() {
        // Toggle Center class
        this.$element.toggleClass('owl-center', this.settings.center);

        // if items number is less than in body
        if (this.settings.loop && this._items.length < this.settings.items) {
            this.settings.loop = false;
        }

        if (this.settings.autoWidth) {
            this.settings.stagePadding = false;
            this.settings.merge = false;
        }
    };

    /**
     * Prepares an item before add.
     * @todo Rename event parameter `content` to `item`.
     * @protected
     * @returns {jQuery|HTMLElement} - The item container.
     */
    Owl.prototype.prepare = function(item) {
        var event = this.trigger('prepare', { content: item });

        if (!event.data) {
            event.data = $('<' + this.settings.itemElement + '/>')
                .addClass(this.settings.itemClass).append(item)
        }

        this.trigger('prepared', { content: event.data });

        return event.data;
    };

    /**
     * Updates the view.
     * @public
     */
    Owl.prototype.update = function() {
        var i = 0,
            n = this._pipe.length,
            filter = $.proxy(function(p) { return this[p] }, this._invalidated),
            cache = {};

        while (i < n) {
            if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
                this._pipe[i].run(cache);
            }
            i++;
        }

        this._invalidated = {};
    };

    /**
     * Gets the width of the view.
     * @public
     * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
     * @returns {Number} - The width of the view in pixel.
     */
    Owl.prototype.width = function(dimension) {
        dimension = dimension || Owl.Width.Default;
        switch (dimension) {
            case Owl.Width.Inner:
            case Owl.Width.Outer:
                return this._width;
            default:
                return this._width - this.settings.stagePadding * 2 + this.settings.margin;
        }
    };

    /**
     * Refreshes the carousel primarily for adaptive purposes.
     * @public
     */
    Owl.prototype.refresh = function() {
        if (this._items.length === 0) {
            return false;
        }

        var start = new Date().getTime();

        this.trigger('refresh');

        this.setup();

        this.optionsLogic();

        // hide and show methods helps here to set a proper widths,
        // this prevents scrollbar to be calculated in stage width
        this.$stage.addClass('owl-refresh');

        this.update();

        this.$stage.removeClass('owl-refresh');

        this.state.orientation = window.orientation;

        this.watchVisibility();

        this.trigger('refreshed');
    };

    /**
     * Save internal event references and add event based functions.
     * @protected
     */
    Owl.prototype.eventsCall = function() {
        // Save events references
        this.e._onDragStart = $.proxy(function(e) {
            this.onDragStart(e);
        }, this);
        this.e._onDragMove = $.proxy(function(e) {
            this.onDragMove(e);
        }, this);
        this.e._onDragEnd = $.proxy(function(e) {
            this.onDragEnd(e);
        }, this);
        this.e._onResize = $.proxy(function(e) {
            this.onResize(e);
        }, this);
        this.e._transitionEnd = $.proxy(function(e) {
            this.transitionEnd(e);
        }, this);
        this.e._preventClick = $.proxy(function(e) {
            this.preventClick(e);
        }, this);
    };

    /**
     * Checks window `resize` event.
     * @protected
     */
    Owl.prototype.onThrottledResize = function() {
        window.clearTimeout(this.resizeTimer);
        this.resizeTimer = window.setTimeout(this.e._onResize, this.settings.responsiveRefreshRate);
    };

    /**
     * Checks window `resize` event.
     * @protected
     */
    Owl.prototype.onResize = function() {
        if (!this._items.length) {
            return false;
        }

        if (this._width === this.$element.width()) {
            return false;
        }

        if (this.trigger('resize').isDefaultPrevented()) {
            return false;
        }

        this._width = this.$element.width();

        this.invalidate('width');

        this.refresh();

        this.trigger('resized');
    };

    /**
     * Checks for touch/mouse drag event type and add run event handlers.
     * @protected
     */
    Owl.prototype.eventsRouter = function(event) {
        var type = event.type;

        if (type === "mousedown" || type === "touchstart") {
            this.onDragStart(event);
        } else if (type === "mousemove" || type === "touchmove") {
            this.onDragMove(event);
        } else if (type === "mouseup" || type === "touchend") {
            this.onDragEnd(event);
        } else if (type === "touchcancel") {
            this.onDragEnd(event);
        }
    };

    /**
     * Checks for touch/mouse drag options and add necessery event handlers.
     * @protected
     */
    Owl.prototype.internalEvents = function() {
        var isTouch = isTouchSupport(),
            isTouchIE = isTouchSupportIE();

        if (this.settings.mouseDrag){
            this.$stage.on('mousedown', $.proxy(function(event) { this.eventsRouter(event) }, this));
            this.$stage.on('dragstart', function() { return false });
            this.$stage.get(0).onselectstart = function() { return false };
        } else {
            this.$element.addClass('owl-text-select-on');
        }

        if (this.settings.touchDrag && !isTouchIE){
            this.$stage.on('touchstart touchcancel', $.proxy(function(event) { this.eventsRouter(event) }, this));
        }

        // catch transitionEnd event
        if (this.transitionEndVendor) {
            this.on(this.$stage.get(0), this.transitionEndVendor, this.e._transitionEnd, false);
        }

        // responsive
        if (this.settings.responsive !== false) {
            this.on(window, 'resize', $.proxy(this.onThrottledResize, this));
        }
    };

    /**
     * Handles touchstart/mousedown event.
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.onDragStart = function(event) {
        var ev, isTouchEvent, pageX, pageY, animatedPos;

        ev = event.originalEvent || event || window.event;

        // prevent right click
        if (ev.which === 3 || this.state.isTouch) {
            return false;
        }

        if (ev.type === 'mousedown') {
            this.$stage.addClass('owl-grab');
        }

        this.trigger('drag');
        this.drag.startTime = new Date().getTime();
        this.speed(0);
        this.state.isTouch = true;
        this.state.isScrolling = false;
        this.state.isSwiping = false;
        this.drag.distance = 0;

        pageX = getTouches(ev).x;
        pageY = getTouches(ev).y;

        // get stage position left
        this.drag.offsetX = this.$stage.position().left;
        this.drag.offsetY = this.$stage.position().top;

        if (this.settings.rtl) {
            this.drag.offsetX = this.$stage.position().left + this.$stage.width() - this.width()
                + this.settings.margin;
        }

        // catch position // ie to fix
        if (this.state.inMotion && this.support3d) {
            animatedPos = this.getTransformProperty();
            this.drag.offsetX = animatedPos;
            this.animate(animatedPos);
            this.state.inMotion = true;
        } else if (this.state.inMotion && !this.support3d) {
            this.state.inMotion = false;
            return false;
        }

        this.drag.startX = pageX - this.drag.offsetX;
        this.drag.startY = pageY - this.drag.offsetY;

        this.drag.start = pageX - this.drag.startX;
        this.drag.targetEl = ev.target || ev.srcElement;
        this.drag.updatedX = this.drag.start;

        // to do/check
        // prevent links and images dragging;
        if (this.drag.targetEl.tagName === "IMG" || this.drag.targetEl.tagName === "A") {
            this.drag.targetEl.draggable = false;
        }

        $(document).on('mousemove.owl.dragEvents mouseup.owl.dragEvents touchmove.owl.dragEvents touchend.owl.dragEvents', $.proxy(function(event) {this.eventsRouter(event)},this));
    };

    /**
     * Handles the touchmove/mousemove events.
     * @todo Simplify
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.onDragMove = function(event) {
        var ev, isTouchEvent, pageX, pageY, minValue, maxValue, pull;

        if (!this.state.isTouch) {
            return;
        }

        if (this.state.isScrolling) {
            return;
        }

        ev = event.originalEvent || event || window.event;

        pageX = getTouches(ev).x;
        pageY = getTouches(ev).y;

        // Drag Direction
        this.drag.currentX = pageX - this.drag.startX;
        this.drag.currentY = pageY - this.drag.startY;
        this.drag.distance = this.drag.currentX - this.drag.offsetX;

        // Check move direction
        if (this.drag.distance < 0) {
            this.state.direction = this.settings.rtl ? 'right' : 'left';
        } else if (this.drag.distance > 0) {
            this.state.direction = this.settings.rtl ? 'left' : 'right';
        }
        // Loop
        if (this.settings.loop) {
            if (this.op(this.drag.currentX, '>', this.coordinates(this.minimum())) && this.state.direction === 'right') {
                this.drag.currentX -= (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length);
            } else if (this.op(this.drag.currentX, '<', this.coordinates(this.maximum())) && this.state.direction === 'left') {
                this.drag.currentX += (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length);
            }
        } else {
            // pull
            minValue = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
            maxValue = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
            pull = this.settings.pullDrag ? this.drag.distance / 5 : 0;
            this.drag.currentX = Math.max(Math.min(this.drag.currentX, minValue + pull), maxValue + pull);
        }

        // Lock browser if swiping horizontal

        if ((this.drag.distance > 8 || this.drag.distance < -8)) {
            if (ev.preventDefault !== undefined) {
                ev.preventDefault();
            } else {
                ev.returnValue = false;
            }
            this.state.isSwiping = true;
        }

        this.drag.updatedX = this.drag.currentX;

        // Lock Owl if scrolling
        if ((this.drag.currentY > 16 || this.drag.currentY < -16) && this.state.isSwiping === false) {
            this.state.isScrolling = true;
            this.drag.updatedX = this.drag.start;
        }

        this.animate(this.drag.updatedX);
    };

    /**
     * Handles the touchend/mouseup events.
     * @protected
     */
    Owl.prototype.onDragEnd = function(event) {
        var compareTimes, distanceAbs, closest;

        if (!this.state.isTouch) {
            return;
        }

        if (event.type === 'mouseup') {
            this.$stage.removeClass('owl-grab');
        }

        this.trigger('dragged');

        // prevent links and images dragging;
        this.drag.targetEl.removeAttribute("draggable");

        // remove drag event listeners

        this.state.isTouch = false;
        this.state.isScrolling = false;
        this.state.isSwiping = false;

        // to check
        if (this.drag.distance === 0 && this.state.inMotion !== true) {
            this.state.inMotion = false;
            return false;
        }

        // prevent clicks while scrolling

        this.drag.endTime = new Date().getTime();
        compareTimes = this.drag.endTime - this.drag.startTime;
        distanceAbs = Math.abs(this.drag.distance);

        // to test
        if (distanceAbs > 3 || compareTimes > 300) {
            this.removeClick(this.drag.targetEl);
        }

        closest = this.closest(this.drag.updatedX);

        this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
        this.current(closest);
        this.invalidate('position');
        this.update();

        // if pullDrag is off then fire transitionEnd event manually when stick
        // to border
        if (!this.settings.pullDrag && this.drag.updatedX === this.coordinates(closest)) {
            this.transitionEnd();
        }

        this.drag.distance = 0;

        $(document).off('.owl.dragEvents');
    };

    /**
     * Attaches `preventClick` to disable link while swipping.
     * @protected
     * @param {HTMLElement} [target] - The target of the `click` event.
     */
    Owl.prototype.removeClick = function(target) {
        this.drag.targetEl = target;
        $(target).on('click.preventClick', this.e._preventClick);
        // to make sure click is removed:
        window.setTimeout(function() {
            $(target).off('click.preventClick');
        }, 300);
    };

    /**
     * Suppresses click event.
     * @protected
     * @param {Event} ev - The event arguments.
     */
    Owl.prototype.preventClick = function(ev) {
        if (ev.preventDefault) {
            ev.preventDefault();
        } else {
            ev.returnValue = false;
        }
        if (ev.stopPropagation) {
            ev.stopPropagation();
        }
        $(ev.target).off('click.preventClick');
    };

    /**
     * Catches stage position while animate (only CSS3).
     * @protected
     * @returns
     */
    Owl.prototype.getTransformProperty = function() {
        var transform, matrix3d;

        transform = window.getComputedStyle(this.$stage.get(0), null).getPropertyValue(this.vendorName + 'transform');
        // var transform = this.$stage.css(this.vendorName + 'transform')
        transform = transform.replace(/matrix(3d)?\(|\)/g, '').split(',');
        matrix3d = transform.length === 16;

        return matrix3d !== true ? transform[4] : transform[12];
    };

    /**
     * Gets absolute position of the closest item for a coordinate.
     * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
     * @protected
     * @param {Number} coordinate - The coordinate in pixel.
     * @return {Number} - The absolute position of the closest item.
     */
    Owl.prototype.closest = function(coordinate) {
        var position = -1, pull = 30, width = this.width(), coordinates = this.coordinates();

        if (!this.settings.freeDrag) {
            // check closest item
            $.each(coordinates, $.proxy(function(index, value) {
                if (coordinate > value - pull && coordinate < value + pull) {
                    position = index;
                } else if (this.op(coordinate, '<', value)
                    && this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
                    position = this.state.direction === 'left' ? index + 1 : index;
                }
                return position === -1;
            }, this));
        }

        if (!this.settings.loop) {
            // non loop boundries
            if (this.op(coordinate, '>', coordinates[this.minimum()])) {
                position = coordinate = this.minimum();
            } else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
                position = coordinate = this.maximum();
            }
        }

        return position;
    };

    /**
     * Animates the stage.
     * @public
     * @param {Number} coordinate - The coordinate in pixels.
     */
    Owl.prototype.animate = function(coordinate) {
        this.trigger('translate');
        this.state.inMotion = this.speed() > 0;

        if (this.support3d) {
            this.$stage.css({
                transform: 'translate3d(' + coordinate + 'px' + ',0px, 0px)',
                transition: (this.speed() / 1000) + 's'
            });
        } else if (this.state.isTouch) {
            this.$stage.css({
                left: coordinate + 'px'
            });
        } else {
            this.$stage.animate({
                left: coordinate
            }, this.speed() / 1000, this.settings.fallbackEasing, $.proxy(function() {
                if (this.state.inMotion) {
                    this.transitionEnd();
                }
            }, this));
        }
    };

    /**
     * Sets the absolute position of the current item.
     * @public
     * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
     * @returns {Number} - The absolute position of the current item.
     */
    Owl.prototype.current = function(position) {
        if (position === undefined) {
            return this._current;
        }

        if (this._items.length === 0) {
            return undefined;
        }

        position = this.normalize(position);

        if (this._current !== position) {
            var event = this.trigger('change', { property: { name: 'position', value: position } });

            if (event.data !== undefined) {
                position = this.normalize(event.data);
            }

            this._current = position;

            this.invalidate('position');

            this.trigger('changed', { property: { name: 'position', value: this._current } });
        }

        return this._current;
    };

    /**
     * Invalidates the given part of the update routine.
     * @param {String} part - The part to invalidate.
     */
    Owl.prototype.invalidate = function(part) {
        this._invalidated[part] = true;
    }

    /**
     * Resets the absolute position of the current item.
     * @public
     * @param {Number} position - The absolute position of the new item.
     */
    Owl.prototype.reset = function(position) {
        position = this.normalize(position);

        if (position === undefined) {
            return;
        }

        this._speed = 0;
        this._current = position;

        this.suppress([ 'translate', 'translated' ]);

        this.animate(this.coordinates(position));

        this.release([ 'translate', 'translated' ]);
    };

    /**
     * Normalizes an absolute or a relative position for an item.
     * @public
     * @param {Number} position - The absolute or relative position to normalize.
     * @param {Boolean} [relative=false] - Whether the given position is relative or not.
     * @returns {Number} - The normalized position.
     */
    Owl.prototype.normalize = function(position, relative) {
        var n = (relative ? this._items.length : this._items.length + this._clones.length);

        if (!$.isNumeric(position) || n < 1) {
            return undefined;
        }

        if (this._clones.length) {
            position = ((position % n) + n) % n;
        } else {
            position = Math.max(this.minimum(relative), Math.min(this.maximum(relative), position));
        }

        return position;
    };

    /**
     * Converts an absolute position for an item into a relative position.
     * @public
     * @param {Number} position - The absolute position to convert.
     * @returns {Number} - The converted position.
     */
    Owl.prototype.relative = function(position) {
        position = this.normalize(position);
        position = position - this._clones.length / 2;
        return this.normalize(position, true);
    };

    /**
     * Gets the maximum position for an item.
     * @public
     * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
     * @returns {Number}
     */
    Owl.prototype.maximum = function(relative) {
        var maximum, width, i = 0, coordinate,
            settings = this.settings;

        if (relative) {
            return this._items.length - 1;
        }

        if (!settings.loop && settings.center) {
            maximum = this._items.length - 1;
        } else if (!settings.loop && !settings.center) {
            maximum = this._items.length - settings.items;
        } else if (settings.loop || settings.center) {
            maximum = this._items.length + settings.items;
        } else if (settings.autoWidth || settings.merge) {
            revert = settings.rtl ? 1 : -1;
            width = this.$stage.width() - this.$element.width();
            while (coordinate = this.coordinates(i)) {
                if (coordinate * revert >= width) {
                    break;
                }
                maximum = ++i;
            }
        } else {
            throw 'Can not detect maximum absolute position.'
        }

        return maximum;
    };

    /**
     * Gets the minimum position for an item.
     * @public
     * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
     * @returns {Number}
     */
    Owl.prototype.minimum = function(relative) {
        if (relative) {
            return 0;
        }

        return this._clones.length / 2;
    };

    /**
     * Gets an item at the specified relative position.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
     */
    Owl.prototype.items = function(position) {
        if (position === undefined) {
            return this._items.slice();
        }

        position = this.normalize(position, true);
        return this._items[position];
    };

    /**
     * Gets an item at the specified relative position.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
     */
    Owl.prototype.mergers = function(position) {
        if (position === undefined) {
            return this._mergers.slice();
        }

        position = this.normalize(position, true);
        return this._mergers[position];
    };

    /**
     * Gets the absolute positions of clones for an item.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
     */
    Owl.prototype.clones = function(position) {
        var odd = this._clones.length / 2,
            even = odd + this._items.length,
            map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };

        if (position === undefined) {
            return $.map(this._clones, function(v, i) { return map(i) });
        }

        return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
    };

    /**
     * Sets the current animation speed.
     * @public
     * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
     * @returns {Number} - The current animation speed in milliseconds.
     */
    Owl.prototype.speed = function(speed) {
        if (speed !== undefined) {
            this._speed = speed;
        }

        return this._speed;
    };

    /**
     * Gets the coordinate of an item.
     * @todo The name of this method is missleanding.
     * @public
     * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
     * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
     */
    Owl.prototype.coordinates = function(position) {
        var coordinate = null;

        if (position === undefined) {
            return $.map(this._coordinates, $.proxy(function(coordinate, index) {
                return this.coordinates(index);
            }, this));
        }

        if (this.settings.center) {
            coordinate = this._coordinates[position];
            coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
        } else {
            coordinate = this._coordinates[position - 1] || 0;
        }

        return coordinate;
    };

    /**
     * Calculates the speed for a translation.
     * @protected
     * @param {Number} from - The absolute position of the start item.
     * @param {Number} to - The absolute position of the target item.
     * @param {Number} [factor=undefined] - The time factor in milliseconds.
     * @returns {Number} - The time in milliseconds for the translation.
     */
    Owl.prototype.duration = function(from, to, factor) {
        return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
    };

    /**
     * Slides to the specified item.
     * @public
     * @param {Number} position - The position of the item.
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.to = function(position, speed) {
        if (this.settings.loop) {
            var distance = position - this.relative(this.current()),
                revert = this.current(),
                before = this.current(),
                after = this.current() + distance,
                direction = before - after < 0 ? true : false,
                items = this._clones.length + this._items.length;

            if (after < this.settings.items && direction === false) {
                revert = before + this._items.length;
                this.reset(revert);
            } else if (after >= items - this.settings.items && direction === true) {
                revert = before - this._items.length;
                this.reset(revert);
            }
            window.clearTimeout(this.e._goToLoop);
            this.e._goToLoop = window.setTimeout($.proxy(function() {
                this.speed(this.duration(this.current(), revert + distance, speed));
                this.current(revert + distance);
                this.update();
            }, this), 30);
        } else {
            this.speed(this.duration(this.current(), position, speed));
            this.current(position);
            this.update();
        }
    };

    /**
     * Slides to the next item.
     * @public
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.next = function(speed) {
        speed = speed || false;
        this.to(this.relative(this.current()) + 1, speed);
    };

    /**
     * Slides to the previous item.
     * @public
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.prev = function(speed) {
        speed = speed || false;
        this.to(this.relative(this.current()) - 1, speed);
    };

    /**
     * Handles the end of an animation.
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.transitionEnd = function(event) {

        // if css2 animation then event object is undefined
        if (event !== undefined) {
            event.stopPropagation();

            // Catch only owl-stage transitionEnd event
            if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
                return false;
            }
        }

        this.state.inMotion = false;
        this.trigger('translated');
    };

    /**
     * Gets viewport width.
     * @protected
     * @return {Number} - The width in pixel.
     */
    Owl.prototype.viewport = function() {
        var width;
        if (this.options.responsiveBaseElement !== window) {
            width = $(this.options.responsiveBaseElement).width();
        } else if (window.innerWidth) {
            width = window.innerWidth;
        } else if (document.documentElement && document.documentElement.clientWidth) {
            width = document.documentElement.clientWidth;
        } else {
            throw 'Can not detect viewport width.';
        }
        return width;
    };

    /**
     * Replaces the current content.
     * @public
     * @param {HTMLElement|jQuery|String} content - The new content.
     */
    Owl.prototype.replace = function(content) {
        this.$stage.empty();
        this._items = [];

        if (content) {
            content = (content instanceof jQuery) ? content : $(content);
        }

        if (this.settings.nestedItemSelector) {
            content = content.find('.' + this.settings.nestedItemSelector);
        }

        content.filter(function() {
            return this.nodeType === 1;
        }).each($.proxy(function(index, item) {
            item = this.prepare(item);
            this.$stage.append(item);
            this._items.push(item);
            this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
        }, this));

        this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);

        this.invalidate('items');
    };

    /**
     * Adds an item.
     * @todo Use `item` instead of `content` for the event arguments.
     * @public
     * @param {HTMLElement|jQuery|String} content - The item content to add.
     * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
     */
    Owl.prototype.add = function(content, position) {
        position = position === undefined ? this._items.length : this.normalize(position, true);

        this.trigger('add', { content: content, position: position });

        if (this._items.length === 0 || position === this._items.length) {
            this.$stage.append(content);
            this._items.push(content);
            this._mergers.push(content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
        } else {
            this._items[position].before(content);
            this._items.splice(position, 0, content);
            this._mergers.splice(position, 0, content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
        }

        this.invalidate('items');

        this.trigger('added', { content: content, position: position });
    };

    /**
     * Removes an item by its position.
     * @todo Use `item` instead of `content` for the event arguments.
     * @public
     * @param {Number} position - The relative position of the item to remove.
     */
    Owl.prototype.remove = function(position) {
        position = this.normalize(position, true);

        if (position === undefined) {
            return;
        }

        this.trigger('remove', { content: this._items[position], position: position });

        this._items[position].remove();
        this._items.splice(position, 1);
        this._mergers.splice(position, 1);

        this.invalidate('items');

        this.trigger('removed', { content: null, position: position });
    };

    /**
     * Adds triggerable events.
     * @protected
     */
    Owl.prototype.addTriggerableEvents = function() {
        var handler = $.proxy(function(callback, event) {
            return $.proxy(function(e) {
                if (e.relatedTarget !== this) {
                    this.suppress([ event ]);
                    callback.apply(this, [].slice.call(arguments, 1));
                    this.release([ event ]);
                }
            }, this);
        }, this);

        $.each({
            'next': this.next,
            'prev': this.prev,
            'to': this.to,
            'destroy': this.destroy,
            'refresh': this.refresh,
            'replace': this.replace,
            'add': this.add,
            'remove': this.remove
        }, $.proxy(function(event, callback) {
            this.$element.on(event + '.owl.carousel', handler(callback, event + '.owl.carousel'));
        }, this));

    };

    /**
     * Watches the visibility of the carousel element.
     * @protected
     */
    Owl.prototype.watchVisibility = function() {

        // test on zepto
        if (!isElVisible(this.$element.get(0))) {
            this.$element.addClass('owl-hidden');
            window.clearInterval(this.e._checkVisibile);
            this.e._checkVisibile = window.setInterval($.proxy(checkVisible, this), 500);
        }

        function isElVisible(el) {
            return el.offsetWidth > 0 && el.offsetHeight > 0;
        }

        function checkVisible() {
            if (isElVisible(this.$element.get(0))) {
                this.$element.removeClass('owl-hidden');
                this.refresh();
                window.clearInterval(this.e._checkVisibile);
            }
        }
    };

    /**
     * Preloads images with auto width.
     * @protected
     * @todo Still to test
     */
    Owl.prototype.preloadAutoWidthImages = function(imgs) {
        var loaded, that, $el, img;

        loaded = 0;
        that = this;
        imgs.each(function(i, el) {
            $el = $(el);
            img = new Image();

            img.onload = function() {
                loaded++;
                $el.attr('src', img.src);
                $el.css('opacity', 1);
                if (loaded >= imgs.length) {
                    that.state.imagesLoaded = true;
                    that.initialize();
                }
            };

            img.src = $el.attr('src') || $el.attr('data-src') || $el.attr('data-src-retina');
        });
    };

    /**
     * Destroys the carousel.
     * @public
     */
    Owl.prototype.destroy = function() {

        if (this.$element.hasClass(this.settings.themeClass)) {
            this.$element.removeClass(this.settings.themeClass);
        }

        if (this.settings.responsive !== false) {
            $(window).off('resize.owl.carousel');
        }

        if (this.transitionEndVendor) {
            this.off(this.$stage.get(0), this.transitionEndVendor, this.e._transitionEnd);
        }

        for ( var i in this._plugins) {
            this._plugins[i].destroy();
        }

        if (this.settings.mouseDrag || this.settings.touchDrag) {
            this.$stage.off('mousedown touchstart touchcancel');
            $(document).off('.owl.dragEvents');
            this.$stage.get(0).onselectstart = function() {};
            this.$stage.off('dragstart', function() { return false });
        }

        // remove event handlers in the ".owl.carousel" namespace
        this.$element.off('.owl');

        this.$stage.children('.cloned').remove();
        this.e = null;
        this.$element.removeData('owlCarousel');

        this.$stage.children().contents().unwrap();
        this.$stage.children().unwrap();
        this.$stage.unwrap();
    };

    /**
     * Operators to calculate right-to-left and left-to-right.
     * @protected
     * @param {Number} [a] - The left side operand.
     * @param {String} [o] - The operator.
     * @param {Number} [b] - The right side operand.
     */
    Owl.prototype.op = function(a, o, b) {
        var rtl = this.settings.rtl;
        switch (o) {
            case '<':
                return rtl ? a > b : a < b;
            case '>':
                return rtl ? a < b : a > b;
            case '>=':
                return rtl ? a <= b : a >= b;
            case '<=':
                return rtl ? a >= b : a <= b;
            default:
                break;
        }
    };

    /**
     * Attaches to an internal event.
     * @protected
     * @param {HTMLElement} element - The event source.
     * @param {String} event - The event name.
     * @param {Function} listener - The event handler to attach.
     * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
     */
    Owl.prototype.on = function(element, event, listener, capture) {
        if (element.addEventListener) {
            element.addEventListener(event, listener, capture);
        } else if (element.attachEvent) {
            element.attachEvent('on' + event, listener);
        }
    };

    /**
     * Detaches from an internal event.
     * @protected
     * @param {HTMLElement} element - The event source.
     * @param {String} event - The event name.
     * @param {Function} listener - The attached event handler to detach.
     * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
     */
    Owl.prototype.off = function(element, event, listener, capture) {
        if (element.removeEventListener) {
            element.removeEventListener(event, listener, capture);
        } else if (element.detachEvent) {
            element.detachEvent('on' + event, listener);
        }
    };

    /**
     * Triggers an public event.
     * @protected
     * @param {String} name - The event name.
     * @param {*} [data=null] - The event data.
     * @param {String} [namespace=.owl.carousel] - The event namespace.
     * @returns {Event} - The event arguments.
     */
    Owl.prototype.trigger = function(name, data, namespace) {
        var status = {
            item: { count: this._items.length, index: this.current() }
        }, handler = $.camelCase(
            $.grep([ 'on', name, namespace ], function(v) { return v })
                .join('-').toLowerCase()
        ), event = $.Event(
            [ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
            $.extend({ relatedTarget: this }, status, data)
        );

        if (!this._supress[name]) {
            $.each(this._plugins, function(name, plugin) {
                if (plugin.onTrigger) {
                    plugin.onTrigger(event);
                }
            });

            this.$element.trigger(event);

            if (this.settings && typeof this.settings[handler] === 'function') {
                this.settings[handler].apply(this, event);
            }
        }

        return event;
    };

    /**
     * Suppresses events.
     * @protected
     * @param {Array.<String>} events - The events to suppress.
     */
    Owl.prototype.suppress = function(events) {
        $.each(events, $.proxy(function(index, event) {
            this._supress[event] = true;
        }, this));
    };

    /**
     * Releases suppressed events.
     * @protected
     * @param {Array.<String>} events - The events to release.
     */
    Owl.prototype.release = function(events) {
        $.each(events, $.proxy(function(index, event) {
            delete this._supress[event];
        }, this));
    };

    /**
     * Checks the availability of some browser features.
     * @protected
     */
    Owl.prototype.browserSupport = function() {
        this.support3d = isPerspective();

        if (this.support3d) {
            this.transformVendor = isTransform();

            // take transitionend event name by detecting transition
            var endVendors = [ 'transitionend', 'webkitTransitionEnd', 'transitionend', 'oTransitionEnd' ];
            this.transitionEndVendor = endVendors[isTransition()];

            // take vendor name from transform name
            this.vendorName = this.transformVendor.replace(/Transform/i, '');
            this.vendorName = this.vendorName !== '' ? '-' + this.vendorName.toLowerCase() + '-' : '';
        }

        this.state.orientation = window.orientation;
    };

    /**
     * Get touch/drag coordinats.
     * @private
     * @param {event} - mousedown/touchstart event
     * @returns {object} - Contains X and Y of current mouse/touch position
     */

    function getTouches(event) {
        if (event.touches !== undefined) {
            return {
                x: event.touches[0].pageX,
                y: event.touches[0].pageY
            };
        }

        if (event.touches === undefined) {
            if (event.pageX !== undefined) {
                return {
                    x: event.pageX,
                    y: event.pageY
                };
            }

            if (event.pageX === undefined) {
                return {
                    x: event.clientX,
                    y: event.clientY
                };
            }
        }
    }

    /**
     * Checks for CSS support.
     * @private
     * @param {Array} array - The CSS properties to check for.
     * @returns {Array} - Contains the supported CSS property name and its index or `false`.
     */
    function isStyleSupported(array) {
        var p, s, fake = document.createElement('div'), list = array;
        for (p in list) {
            s = list[p];
            if (typeof fake.style[s] !== 'undefined') {
                fake = null;
                return [ s, p ];
            }
        }
        return [ false ];
    }

    /**
     * Checks for CSS transition support.
     * @private
     * @todo Realy bad design
     * @returns {Number}
     */
    function isTransition() {
        return isStyleSupported([ 'transition', 'WebkitTransition', 'MozTransition', 'OTransition' ])[1];
    }

    /**
     * Checks for CSS transform support.
     * @private
     * @returns {String} The supported property name or false.
     */
    function isTransform() {
        return isStyleSupported([ 'transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ])[0];
    }

    /**
     * Checks for CSS perspective support.
     * @private
     * @returns {String} The supported property name or false.
     */
    function isPerspective() {
        return isStyleSupported([ 'perspective', 'webkitPerspective', 'MozPerspective', 'OPerspective', 'MsPerspective' ])[0];
    }

    /**
     * Checks wether touch is supported or not.
     * @private
     * @returns {Boolean}
     */
    function isTouchSupport() {
        return 'ontouchstart' in window || !!(navigator.msMaxTouchPoints);
    }

    /**
     * Checks wether touch is supported or not for IE.
     * @private
     * @returns {Boolean}
     */
    function isTouchSupportIE() {
        return window.navigator.msPointerEnabled;
    }

    /**
     * The jQuery Plugin for the Owl Carousel
     * @public
     */
    $.fn.owlCarousel = function(options) {
        return this.each(function() {
            if (!$(this).data('owlCarousel')) {
                $(this).data('owlCarousel', new Owl(this, options));
            }
        });
    };

    /**
     * The constructor for the jQuery Plugin
     * @public
     */
    $.fn.owlCarousel.Constructor = Owl;

    /**
     * Lazy Plugin
     * @version 2.0.0
     * @author Bartosz Wojciechowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the lazy plugin.
     * @class The Lazy Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Lazy = function(carousel) {

        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Already loaded items.
         * @protected
         * @type {Array.<jQuery>}
         */
        this._loaded = [];

        /**
         * Event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
                if (!e.namespace) {
                    return;
                }

                if (!this._core.settings || !this._core.settings.lazyLoad) {
                    return;
                }

                if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
                    var settings = this._core.settings,
                        n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
                        i = ((settings.center && n * -1) || 0),
                        position = ((e.property && e.property.value) || this._core.current()) + i,
                        clones = this._core.clones().length,
                        load = $.proxy(function(i, v) { this.load(v) }, this);

                    /** while (i++ < n) { */
                    /** quick fix for stagePadding > 0 */
                    while (i++ <= n) {
                        if (clones) {
                            this.load(clones / 2 + this._core.relative(position));
                        } else {
                            this.load(position-1);
                        }
                        position++;
                        clones && $.each(this._core.clones(this._core.relative(position)), load);
                    }
                }
            }, this)
        };

        // set the default options
        this._core.options = $.extend({}, Lazy.Defaults, this._core.options);

        // register event handler
        this._core.$element.on(this._handlers);
    };

    /**
     * Default options.
     * @public
     */
    Lazy.Defaults = {
        lazyLoad: false
    };

    /**
     * Loads all resources of an item at the specified position.
     * @param {Number} position - The absolute position of the item.
     * @protected
     */
    Lazy.prototype.load = function(position) {
        var $item = this._core.$stage.children().eq(position),
            $elements = $item && $item.find('.owl-lazy');


        if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
            return;
        }

        $elements.each($.proxy(function(index, element) {
            var $element = $(element), image,
                url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');

            this._core.trigger('load', { element: $element, url: url }, 'lazy');

            if ($element.is('img')) {
                $element.one('load.owl.lazy', $.proxy(function() {
                    $element.css('opacity', 1);
                    this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
                }, this)).attr('src', url);
            } else {
                image = new Image();
                image.onload = $.proxy(function() {
                    $element.css({
                        'background-image': 'url(' + url + ')',
                        'opacity': '1'
                    });
                    this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
                }, this);
                image.src = url;
            }
        }, this));

        this._loaded.push($item.get(0));

    };



    /**
     * Destroys the plugin.
     * @public
     */
    Lazy.prototype.destroy = function() {
        var handler, property;

        for (handler in this.handlers) {
            this._core.$element.off(handler, this.handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;

    /**
     * AutoHeight Plugin
     * @version 2.0.0
     * @author Bartosz Wojciechowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the auto height plugin.
     * @class The Auto Height Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var AutoHeight = function(carousel) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel': $.proxy(function() {
                if (this._core.settings.autoHeight) {
                    this.update();
                }
            }, this),
            'changed.owl.carousel': $.proxy(function(e) {
                if (this._core.settings.autoHeight && e.property.name == 'position'){
                    this.update();
                }
            }, this),
            'loaded.owl.lazy': $.proxy(function(e) {
                if (this._core.settings.autoHeight && e.element.closest('.' + this._core.settings.itemClass)
                    === this._core.$stage.children().eq(this._core.current())) {
                    this.update();
                }
            }, this)
        };

        // set default options
        this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);

        // register event handlers
        this._core.$element.on(this._handlers);
    };

    /**
     * Default options.
     * @public
     */
    AutoHeight.Defaults = {
        autoHeight: false,
        autoHeightClass: 'owl-height'
    };

    /**
     * Updates the view.
     */
    AutoHeight.prototype.update = function() {
        this._core.$stage.parent()
            .height(this._core.$stage.children().eq(this._core.current()).height())
            .addClass(this._core.settings.autoHeightClass);
    };

    AutoHeight.prototype.destroy = function() {
        var handler, property;

        for (handler in this._handlers) {
            this._core.$element.off(handler, this._handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;

    /**
     * Video Plugin
     * @version 2.0.0
     * @author Bartosz Wojciechowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the video plugin.
     * @class The Video Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Video = function(carousel) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Cache all video URLs.
         * @protected
         * @type {Object}
         */
        this._videos = {};

        /**
         * Current playing item.
         * @protected
         * @type {jQuery}
         */
        this._playing = null;

        /**
         * Whether this is in fullscreen or not.
         * @protected
         * @type {Boolean}
         */
        this._fullscreen = false;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'resize.owl.carousel': $.proxy(function(e) {
                if (this._core.settings.video && !this.isInFullScreen()) {
                    e.preventDefault();
                }
            }, this),
            'refresh.owl.carousel changed.owl.carousel': $.proxy(function(e) {
                if (this._playing) {
                    this.stop();
                }
            }, this),
            'prepared.owl.carousel': $.proxy(function(e) {
                var $element = $(e.content).find('.owl-video');
                if ($element.length) {
                    $element.css('display', 'none');
                    this.fetch($element, $(e.content));
                }
            }, this)
        };

        // set default options
        this._core.options = $.extend({}, Video.Defaults, this._core.options);

        // register event handlers
        this._core.$element.on(this._handlers);

        this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
            this.play(e);
        }, this));
    };

    /**
     * Default options.
     * @public
     */
    Video.Defaults = {
        video: false,
        videoHeight: false,
        videoWidth: false
    };

    /**
     * Gets the video ID and the type (YouTube/Vimeo only).
     * @protected
     * @param {jQuery} target - The target containing the video data.
     * @param {jQuery} item - The item containing the video.
     */
    Video.prototype.fetch = function(target, item) {

        var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
            id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
            width = target.attr('data-width') || this._core.settings.videoWidth,
            height = target.attr('data-height') || this._core.settings.videoHeight,
            url = target.attr('href');

        if (url) {
            id = url.match(/(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/);

            if (id[3].indexOf('youtu') > -1) {
                type = 'youtube';
            } else if (id[3].indexOf('vimeo') > -1) {
                type = 'vimeo';
            } else {
                throw new Error('Video URL not supported.');
            }
            id = id[6];
        } else {
            throw new Error('Missing video URL.');
        }

        this._videos[url] = {
            type: type,
            id: id,
            width: width,
            height: height
        };

        item.attr('data-video', url);

        this.thumbnail(target, this._videos[url]);
    };

    /**
     * Creates video thumbnail.
     * @protected
     * @param {jQuery} target - The target containing the video data.
     * @param {Object} info - The video info object.
     * @see `fetch`
     */
    Video.prototype.thumbnail = function(target, video) {

        var tnLink,
            icon,
            path,
            dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
            customTn = target.find('img'),
            srcType = 'src',
            lazyClass = '',
            settings = this._core.settings,
            create = function(path) {
                icon = '<div class="owl-video-play-icon"></div>';

                if (settings.lazyLoad) {
                    tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
                } else {
                    tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
                }
                target.after(tnLink);
                target.after(icon);
            };

        // wrap video content into owl-video-wrapper div
        target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');

        if (this._core.settings.lazyLoad) {
            srcType = 'data-src';
            lazyClass = 'owl-lazy';
        }

        // custom thumbnail
        if (customTn.length) {
            create(customTn.attr(srcType));
            customTn.remove();
            return false;
        }

        if (video.type === 'youtube') {
            path = "http://img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
            create(path);
        } else if (video.type === 'vimeo') {
            $.ajax({
                type: 'GET',
                url: 'http://vimeo.com/api/v2/video/' + video.id + '.json',
                jsonp: 'callback',
                dataType: 'jsonp',
                success: function(data) {
                    path = data[0].thumbnail_large;
                    create(path);
                }
            });
        }
    };

    /**
     * Stops the current video.
     * @public
     */
    Video.prototype.stop = function() {
        this._core.trigger('stop', null, 'video');
        this._playing.find('.owl-video-frame').remove();
        this._playing.removeClass('owl-video-playing');
        this._playing = null;
    };

    /**
     * Starts the current video.
     * @public
     * @param {Event} ev - The event arguments.
     */
    Video.prototype.play = function(ev) {
        this._core.trigger('play', null, 'video');

        if (this._playing) {
            this.stop();
        }

        var target = $(ev.target || ev.srcElement),
            item = target.closest('.' + this._core.settings.itemClass),
            video = this._videos[item.attr('data-video')],
            width = video.width || '100%',
            height = video.height || this._core.$stage.height(),
            html, wrap;

        if (video.type === 'youtube') {
            html = '<iframe width="' + width + '" height="' + height + '" src="http://www.youtube.com/embed/'
                + video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
        } else if (video.type === 'vimeo') {
            html = '<iframe src="http://player.vimeo.com/video/' + video.id + '?autoplay=1" width="' + width
                + '" height="' + height
                + '" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
        }

        item.addClass('owl-video-playing');
        this._playing = item;

        wrap = $('<div style="height:' + height + 'px; width:' + width + 'px" class="owl-video-frame">'
            + html + '</div>');
        target.after(wrap);
    };

    /**
     * Checks whether an video is currently in full screen mode or not.
     * @todo Bad style because looks like a readonly method but changes members.
     * @protected
     * @returns {Boolean}
     */
    Video.prototype.isInFullScreen = function() {

        // if Vimeo Fullscreen mode
        var element = document.fullscreenElement || document.mozFullScreenElement
            || document.webkitFullscreenElement;

        if (element && $(element).parent().hasClass('owl-video-frame')) {
            this._core.speed(0);
            this._fullscreen = true;
        }

        if (element && this._fullscreen && this._playing) {
            return false;
        }

        // comming back from fullscreen
        if (this._fullscreen) {
            this._fullscreen = false;
            return false;
        }

        // check full screen mode and window orientation
        if (this._playing) {
            if (this._core.state.orientation !== window.orientation) {
                this._core.state.orientation = window.orientation;
                return false;
            }
        }

        return true;
    };

    /**
     * Destroys the plugin.
     */
    Video.prototype.destroy = function() {
        var handler, property;

        this._core.$element.off('click.owl.video');

        for (handler in this._handlers) {
            this._core.$element.off(handler, this._handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Video = Video;

    /**
     * Animate Plugin
     * @version 2.0.0
     * @author Bartosz Wojciechowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the animate plugin.
     * @class The Navigation Plugin
     * @param {Owl} scope - The Owl Carousel
     */
    var Animate = function(scope) {
        this.core = scope;
        this.core.options = $.extend({}, Animate.Defaults, this.core.options);
        this.swapping = true;
        this.previous = undefined;
        this.next = undefined;

        this.handlers = {
            'change.owl.carousel': $.proxy(function(e) {
                if (e.property.name == 'position') {
                    this.previous = this.core.current();
                    this.next = e.property.value;
                }
            }, this),
            'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
                this.swapping = e.type == 'translated';
            }, this),
            'translate.owl.carousel': $.proxy(function(e) {
                if (this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
                    this.swap();
                }
            }, this)
        };

        this.core.$element.on(this.handlers);
    };

    /**
     * Default options.
     * @public
     */
    Animate.Defaults = {
        animateOut: false,
        animateIn: false
    };

    /**
     * Toggles the animation classes whenever an translations starts.
     * @protected
     * @returns {Boolean|undefined}
     */
    Animate.prototype.swap = function() {

        if (this.core.settings.items !== 1 || !this.core.support3d) {
            return;
        }

        this.core.speed(0);

        var left,
            clear = $.proxy(this.clear, this),
            previous = this.core.$stage.children().eq(this.previous),
            next = this.core.$stage.children().eq(this.next),
            incoming = this.core.settings.animateIn,
            outgoing = this.core.settings.animateOut;

        if (this.core.current() === this.previous) {
            return;
        }

        if (outgoing) {
            left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
            previous.css( { 'left': left + 'px' } )
                .addClass('animated owl-animated-out')
                .addClass(outgoing)
                .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear);
        }

        if (incoming) {
            next.addClass('animated owl-animated-in')
                .addClass(incoming)
                .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear);
        }
    };

    Animate.prototype.clear = function(e) {
        $(e.target).css( { 'left': '' } )
            .removeClass('animated owl-animated-out owl-animated-in')
            .removeClass(this.core.settings.animateIn)
            .removeClass(this.core.settings.animateOut);
        this.core.transitionEnd();
    };

    /**
     * Destroys the plugin.
     * @public
     */
    Animate.prototype.destroy = function() {
        var handler, property;

        for (handler in this.handlers) {
            this.core.$element.off(handler, this.handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Animate = Animate;

    /**
     * Autoplay Plugin
     * @version 2.0.0
     * @author Bartosz Wojciechowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the autoplay plugin.
     * @class The Autoplay Plugin
     * @param {Owl} scope - The Owl Carousel
     */
    var Autoplay = function(scope) {
        this.core = scope;
        this.core.options = $.extend({}, Autoplay.Defaults, this.core.options);

        this.handlers = {
            'translated.owl.carousel refreshed.owl.carousel': $.proxy(function() {
                this.autoplay();
            }, this),
            'play.owl.autoplay': $.proxy(function(e, t, s) {
                this.play(t, s);
            }, this),
            'stop.owl.autoplay': $.proxy(function() {
                this.stop();
            }, this),
            'mouseover.owl.autoplay': $.proxy(function() {
                if (this.core.settings.autoplayHoverPause) {
                    this.pause();
                }
            }, this),
            'mouseleave.owl.autoplay': $.proxy(function() {
                if (this.core.settings.autoplayHoverPause) {
                    this.autoplay();
                }
            }, this)
        };

        this.core.$element.on(this.handlers);
    };

    /**
     * Default options.
     * @public
     */
    Autoplay.Defaults = {
        autoplay: false,
        autoplayTimeout: 5000,
        autoplayHoverPause: false,
        autoplaySpeed: false
    };

    /**
     * @protected
     * @todo Must be documented.
     */
    Autoplay.prototype.autoplay = function() {
        if (this.core.settings.autoplay && !this.core.state.videoPlay) {
            window.clearInterval(this.interval);

            this.interval = window.setInterval($.proxy(function() {
                this.play();
            }, this), this.core.settings.autoplayTimeout);
        } else {
            window.clearInterval(this.interval);
        }
    };

    /**
     * Starts the autoplay.
     * @public
     * @param {Number} [timeout] - ...
     * @param {Number} [speed] - ...
     * @returns {Boolean|undefined} - ...
     * @todo Must be documented.
     */
    Autoplay.prototype.play = function(timeout, speed) {
        // if tab is inactive - doesnt work in <IE10
        if (document.hidden === true) {
            return;
        }

        if (this.core.state.isTouch || this.core.state.isScrolling
            || this.core.state.isSwiping || this.core.state.inMotion) {
            return;
        }

        if (this.core.settings.autoplay === false) {
            window.clearInterval(this.interval);
            return;
        }

        this.core.next(this.core.settings.autoplaySpeed);
    };

    /**
     * Stops the autoplay.
     * @public
     */
    Autoplay.prototype.stop = function() {
        window.clearInterval(this.interval);
    };

    /**
     * Pauses the autoplay.
     * @public
     */
    Autoplay.prototype.pause = function() {
        window.clearInterval(this.interval);
    };

    /**
     * Destroys the plugin.
     */
    Autoplay.prototype.destroy = function() {
        var handler, property;

        window.clearInterval(this.interval);

        for (handler in this.handlers) {
            this.core.$element.off(handler, this.handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;

    /**
     * Navigation Plugin
     * @version 2.0.0
     * @author Artus Kolanowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the navigation plugin.
     * @class The Navigation Plugin
     * @param {Owl} carousel - The Owl Carousel.
     */
    var Navigation = function(carousel) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Indicates whether the plugin is initialized or not.
         * @protected
         * @type {Boolean}
         */
        this._initialized = false;

        /**
         * The current paging indexes.
         * @protected
         * @type {Array}
         */
        this._pages = [];

        /**
         * All DOM elements of the user interface.
         * @protected
         * @type {Object}
         */
        this._controls = {};

        /**
         * Markup for an indicator.
         * @protected
         * @type {Array.<String>}
         */
        this._templates = [];

        /**
         * The carousel element.
         * @type {jQuery}
         */
        this.$element = this._core.$element;

        /**
         * Overridden methods of the carousel.
         * @protected
         * @type {Object}
         */
        this._overrides = {
            next: this._core.next,
            prev: this._core.prev,
            to: this._core.to
        };

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'prepared.owl.carousel': $.proxy(function(e) {
                if (this._core.settings.dotsData) {
                    this._templates.push($(e.content).find('[data-dot]').addBack('[data-dot]').attr('data-dot'));
                }
            }, this),
            'add.owl.carousel': $.proxy(function(e) {
                if (this._core.settings.dotsData) {
                    this._templates.splice(e.position, 0, $(e.content).find('[data-dot]').addBack('[data-dot]').attr('data-dot'));
                }
            }, this),
            'remove.owl.carousel prepared.owl.carousel': $.proxy(function(e) {
                if (this._core.settings.dotsData) {
                    this._templates.splice(e.position, 1);
                }
            }, this),
            'change.owl.carousel': $.proxy(function(e) {
                if (e.property.name == 'position') {
                    if (!this._core.state.revert && !this._core.settings.loop && this._core.settings.navRewind) {
                        var current = this._core.current(),
                            maximum = this._core.maximum(),
                            minimum = this._core.minimum();
                        e.data = e.property.value > maximum
                            ? current >= maximum ? minimum : maximum
                            : e.property.value < minimum ? maximum : e.property.value;
                    }
                }
            }, this),
            'changed.owl.carousel': $.proxy(function(e) {
                if (e.property.name == 'position') {
                    this.draw();
                }
            }, this),
            'refreshed.owl.carousel': $.proxy(function() {
                if (!this._initialized) {
                    this.initialize();
                    this._initialized = true;
                }
                this._core.trigger('refresh', null, 'navigation');
                this.update();
                this.draw();
                this._core.trigger('refreshed', null, 'navigation');
            }, this)
        };

        // set default options
        this._core.options = $.extend({}, Navigation.Defaults, this._core.options);

        // register event handlers
        this.$element.on(this._handlers);
    };

    /**
     * Default options.
     * @public
     * @todo Rename `slideBy` to `navBy`
     */
    Navigation.Defaults = {
        nav: false,
        navRewind: true,
        navText: [ 'prev', 'next' ],
        navSpeed: false,
        navElement: 'div',
        navContainer: false,
        navContainerClass: 'owl-nav',
        navClass: [ 'owl-prev', 'owl-next' ],
        slideBy: 1,
        dotClass: 'owl-dot',
        dotsClass: 'owl-dots',
        dots: true,
        dotsEach: false,
        dotData: false,
        dotsSpeed: false,
        dotsContainer: false,
        controlsClass: 'owl-controls'
    };

    /**
     * Initializes the layout of the plugin and extends the carousel.
     * @protected
     */
    Navigation.prototype.initialize = function() {
        var $container, override,
            options = this._core.settings;

        // create the indicator template
        if (!options.dotsData) {
            this._templates = [ $('<div>')
                .addClass(options.dotClass)
                .append($('<span>'))
                .prop('outerHTML') ];
        }

        // create controls container if needed
        if (!options.navContainer || !options.dotsContainer) {
            this._controls.$container = $('<div>')
                .addClass(options.controlsClass)
                .appendTo(this.$element);
        }

        // create DOM structure for absolute navigation
        this._controls.$indicators = options.dotsContainer ? $(options.dotsContainer)
            : $('<div>').hide().addClass(options.dotsClass).appendTo(this._controls.$container);

        this._controls.$indicators.on('click', 'div', $.proxy(function(e) {
            var index = $(e.target).parent().is(this._controls.$indicators)
                ? $(e.target).index() : $(e.target).parent().index();

            e.preventDefault();

            this.to(index, options.dotsSpeed);
        }, this));

        // create DOM structure for relative navigation
        $container = options.navContainer ? $(options.navContainer)
            : $('<div>').addClass(options.navContainerClass).prependTo(this._controls.$container);

        this._controls.$next = $('<' + options.navElement + '>');
        this._controls.$previous = this._controls.$next.clone();

        this._controls.$previous
            .addClass(options.navClass[0])
            .html(options.navText[0])
            .hide()
            .prependTo($container)
            .on('click', $.proxy(function(e) {
                this.prev(options.navSpeed);
            }, this));
        this._controls.$next
            .addClass(options.navClass[1])
            .html(options.navText[1])
            .hide()
            .appendTo($container)
            .on('click', $.proxy(function(e) {
                this.next(options.navSpeed);
            }, this));

        // override public methods of the carousel
        for (override in this._overrides) {
            this._core[override] = $.proxy(this[override], this);
        }
    };

    /**
     * Destroys the plugin.
     * @protected
     */
    Navigation.prototype.destroy = function() {
        var handler, control, property, override;

        for (handler in this._handlers) {
            this.$element.off(handler, this._handlers[handler]);
        }
        for (control in this._controls) {
            this._controls[control].remove();
        }
        for (override in this.overides) {
            this._core[override] = this._overrides[override];
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    /**
     * Updates the internal state.
     * @protected
     */
    Navigation.prototype.update = function() {
        var i, j, k,
            options = this._core.settings,
            lower = this._core.clones().length / 2,
            upper = lower + this._core.items().length,
            size = options.center || options.autoWidth || options.dotData
                ? 1 : options.dotsEach || options.items;

        if (options.slideBy !== 'page') {
            options.slideBy = Math.min(options.slideBy, options.items);
        }

        if (options.dots || options.slideBy == 'page') {
            this._pages = [];

            for (i = lower, j = 0, k = 0; i < upper; i++) {
                if (j >= size || j === 0) {
                    this._pages.push({
                        start: i - lower,
                        end: i - lower + size - 1
                    });
                    j = 0, ++k;
                }
                j += this._core.mergers(this._core.relative(i));
            }
        }
    };

    /**
     * Draws the user interface.
     * @todo The option `dotData` wont work.
     * @protected
     */
    Navigation.prototype.draw = function() {
        var difference, i, html = '',
            options = this._core.settings,
            $items = this._core.$stage.children(),
            index = this._core.relative(this._core.current());

        if (options.nav && !options.loop && !options.navRewind) {
            this._controls.$previous.toggleClass('disabled', index <= 0);
            this._controls.$next.toggleClass('disabled', index >= this._core.maximum());
        }

        this._controls.$previous.toggle(options.nav);
        this._controls.$next.toggle(options.nav);

        if (options.dots) {
            difference = this._pages.length - this._controls.$indicators.children().length;

            if (options.dotData && difference !== 0) {
                for (i = 0; i < this._controls.$indicators.children().length; i++) {
                    html += this._templates[this._core.relative(i)];
                }
                this._controls.$indicators.html(html);
            } else if (difference > 0) {
                html = new Array(difference + 1).join(this._templates[0]);
                this._controls.$indicators.append(html);
            } else if (difference < 0) {
                this._controls.$indicators.children().slice(difference).remove();
            }

            this._controls.$indicators.find('.active').removeClass('active');
            this._controls.$indicators.children().eq($.inArray(this.current(), this._pages)).addClass('active');
        }

        this._controls.$indicators.toggle(options.dots);
    };

    /**
     * Extends event data.
     * @protected
     * @param {Event} event - The event object which gets thrown.
     */
    Navigation.prototype.onTrigger = function(event) {
        var settings = this._core.settings;

        event.page = {
            index: $.inArray(this.current(), this._pages),
            count: this._pages.length,
            size: settings && (settings.center || settings.autoWidth || settings.dotData
                ? 1 : settings.dotsEach || settings.items)
        };
    };

    /**
     * Gets the current page position of the carousel.
     * @protected
     * @returns {Number}
     */
    Navigation.prototype.current = function() {
        var index = this._core.relative(this._core.current());
        return $.grep(this._pages, function(o) {
            return o.start <= index && o.end >= index;
        }).pop();
    };

    /**
     * Gets the current succesor/predecessor position.
     * @protected
     * @returns {Number}
     */
    Navigation.prototype.getPosition = function(successor) {
        var position, length,
            options = this._core.settings;

        if (options.slideBy == 'page') {
            position = $.inArray(this.current(), this._pages);
            length = this._pages.length;
            successor ? ++position : --position;
            position = this._pages[((position % length) + length) % length].start;
        } else {
            position = this._core.relative(this._core.current());
            length = this._core.items().length;
            successor ? position += options.slideBy : position -= options.slideBy;
        }
        return position;
    };

    /**
     * Slides to the next item or page.
     * @public
     * @param {Number} [speed=false] - The time in milliseconds for the transition.
     */
    Navigation.prototype.next = function(speed) {
        $.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
    };

    /**
     * Slides to the previous item or page.
     * @public
     * @param {Number} [speed=false] - The time in milliseconds for the transition.
     */
    Navigation.prototype.prev = function(speed) {
        $.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
    };

    /**
     * Slides to the specified item or page.
     * @public
     * @param {Number} position - The position of the item or page.
     * @param {Number} [speed] - The time in milliseconds for the transition.
     * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
     */
    Navigation.prototype.to = function(position, speed, standard) {
        var length;

        if (!standard) {
            length = this._pages.length;
            $.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
        } else {
            $.proxy(this._overrides.to, this._core)(position, speed);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;

    /**
     * Hash Plugin
     * @version 2.0.0
     * @author Artus Kolanowski
     * @license The MIT License (MIT)
     */
    /**
     * Creates the hash plugin.
     * @class The Hash Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Hash = function(carousel) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Hash table for the hashes.
         * @protected
         * @type {Object}
         */
        this._hashes = {};

        /**
         * The carousel element.
         * @type {jQuery}
         */
        this.$element = this._core.$element;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel': $.proxy(function() {
                if (this._core.settings.startPosition == 'URLHash') {
                    $(window).trigger('hashchange.owl.navigation');
                }
            }, this),
            'prepared.owl.carousel': $.proxy(function(e) {
                var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
                this._hashes[hash] = e.content;
            }, this)
        };

        // set default options
        this._core.options = $.extend({}, Hash.Defaults, this._core.options);

        // register the event handlers
        this.$element.on(this._handlers);

        // register event listener for hash navigation
        $(window).on('hashchange.owl.navigation', $.proxy(function() {
            var hash = window.location.hash.substring(1),
                items = this._core.$stage.children(),
                position = this._hashes[hash] && items.index(this._hashes[hash]) || 0;

            if (!hash) {
                return false;
            }

            this._core.to(position, false, true);
        }, this));
    };

    /**
     * Default options.
     * @public
     */
    Hash.Defaults = {
        URLhashListener: false
    };

    /**
     * Destroys the plugin.
     * @public
     */
    Hash.prototype.destroy = function() {
        var handler, property;

        $(window).off('hashchange.owl.navigation');

        for (handler in this._handlers) {
            this._core.$element.off(handler, this._handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] != 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Hash = Hash;

    /**
     * Thumbs Plugin
     * @version 2.0.0
     * @author Gijs RogÃ©
     * @license The MIT License (MIT)
     */
    /**
     * Creates the thumbs plugin.
     * @class The thumbs Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Thumbs = function (carousel) {


        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this.owl = carousel;


        /**
         * All DOM elements for thumbnails
         * @protected
         * @type {Object}
         */
        this._thumbcontent = [];


        /**
         * Instance identiefier
         * @type {number}
         * @private
         */
        this._identifier = 0;


        /**
         * Return current item regardless of clones
         * @protected
         * @type {Object}
         */
        this.owl_currentitem = this.owl.options.startPosition;


        /**
         * The carousel element.
         * @type {jQuery}
         */
        this.$element = this.owl.$element;


        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'prepared.owl.carousel': $.proxy(function (e) {
                if (e.namespace && this.owl.options.thumbs && !this.owl.options.thumbImage && !this.owl.options.thumbsPrerendered && !this.owl.options.thumbImage) {
                    if ($(e.content).find('[data-thumb]').attr('data-thumb') !== undefined) {
                        this._thumbcontent.push($(e.content).find('[data-thumb]').attr('data-thumb'));
                    }
                } else if (e.namespace && this.owl.options.thumbs && this.owl.options.thumbImage) {
                    var innerImage = $(e.content).find('img');
                    this._thumbcontent.push(innerImage);
                }
            }, this),

            'initialized.owl.carousel': $.proxy(function (e) {
                if (e.namespace && this.owl.options.thumbs) {
                    this.render();
                    this.listen();
                    this._identifier = this.owl.$element.data('slider-id');
                    this.setActive();
                }
            }, this),

            'changed.owl.carousel': $.proxy(function (e) {
                if (e.namespace && e.property.name === 'position' && this.owl.options.thumbs) {
                    this._identifier = this.owl.$element.data('slider-id');
                    this.setActive();
                }
            }, this)
        };

        // set default options
        this.owl.options = $.extend({}, Thumbs.Defaults, this.owl.options);

        // register the event handlers
        this.owl.$element.on(this._handlers);
    };


    /**
     * Default options.
     * @public
     */
    Thumbs.Defaults = {
        thumbs: true,
        thumbImage: false,
        thumbContainerClass: 'owl-thumbs',
        thumbItemClass: 'owl-thumb-item',
        moveThumbsInside: false
    };


    /**
     * Listen for thumbnail click
     * @protected
     */
    Thumbs.prototype.listen = function () {

        //set default options
        var options = this.owl.options;

        if (options.thumbsPrerendered) {
            this._thumbcontent._thumbcontainer = $('.' + options.thumbContainerClass);
        }

        //check what thumbitem has been clicked and move slider to that item
        $(this._thumbcontent._thumbcontainer).on('click', this._thumbcontent._thumbcontainer.children(), $.proxy(function (e) {

            // find relative slider
            this._identifier = $(e.target).closest('.' + options.thumbContainerClass).data('slider-id');

            // get index of clicked thumbnail
            var index = $(e.target).parent().is(this._thumbcontent._thumbcontainer) ? $(e.target).index() : $(e.target).closest('.'+options.thumbItemClass).index();

            if (options.thumbsPrerendered) {
                // slide to slide :)
                $('[data-slider-id=' + this._identifier + ']').trigger('to.owl.carousel', [index, options.dotsSpeed, true]);
            } else {
                this.owl.to(index, options.dotsSpeed);
            }

            e.preventDefault();
        }, this));
    };


    /**
     * Builds thumbnails
     * @protected
     */
    Thumbs.prototype.render = function () {

        //set default options
        var options = this.owl.options;

        //create thumbcontainer
        if (!options.thumbsPrerendered) {
            this._thumbcontent._thumbcontainer = $('<div>').addClass(options.thumbContainerClass).appendTo(this.$element);
        } else {
            this._thumbcontent._thumbcontainer = $('.' + options.thumbContainerClass + '');
            if(options.moveThumbsInside){
                this._thumbcontent._thumbcontainer.appendTo(this.$element);
            }
        }

        //create thumb items
        var i;
        if (!options.thumbImage) {
            for (i = 0; i < this._thumbcontent.length; ++i) {
                this._thumbcontent._thumbcontainer.append('<div class=' + options.thumbItemClass + '>' + this._thumbcontent[i] + '</div>');
            }
        } else {
            for (i = 0; i < this._thumbcontent.length; ++i) {
                this._thumbcontent._thumbcontainer.append('<div class=' + options.thumbItemClass + '><img src="' + this._thumbcontent[i].attr('src') + '" alt="' + this._thumbcontent[i].attr('alt') + '" /></div>');
            }
        }
    };


    /**
     * Updates active class on thumbnails
     * @protected
     */
    Thumbs.prototype.setActive = function () {

        // get startslide
        this.owl_currentitem = this.owl._current - (this.owl._clones.length / 2);
        if (this.owl_currentitem === this.owl._items.length) {
            this.owl_currentitem = 0;
        }

        //set default options
        var options = this.owl.options;

        // set relative thumbnail container
        var thumbContainer = options.thumbsPrerendered ? $('.' + options.thumbContainerClass + '[data-slider-id="' + this._identifier + '"]') : this._thumbcontent._thumbcontainer;
        thumbContainer.children().filter('.active').removeClass('active');
        thumbContainer.children().eq(this.owl_currentitem).addClass('active');
    };


    /**
     * Destroys the plugin.
     * @public
     */
    Thumbs.prototype.destroy = function () {
        var handler, property;
        for (handler in this._handlers) {
            this.owl.$element.off(handler, this._handlers[handler]);
        }
        for (property in Object.getOwnPropertyNames(this)) {
            typeof this[property] !== 'function' && (this[property] = null);
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Thumbs = Thumbs;

}));

define('Magento_Theme/js/theme',[
    'jquery',
    'owl_carousel'
], function ($) {


    // Added polyfill to resolve Promise is not done by default

    if (!Promise.allSettled) {
        Promise.allSettled = function (promises) {
            return Promise.all(
                promises.map(p =>
                    Promise.resolve(p).then(
                        value => ({ status: "fulfilled", value }),
                        reason => ({ status: "rejected", reason })
                    )
                )
            );
        };
    }

    if ($(window).width() < 767) {
        startCarousel();
    } else {
        stopCarousel();
    }
    $(window).resize(function () {
        if ($(window).width() < 767) {
            if (!$('.catalog-link-mobile').hasClass('started')) {
                startCarousel();
                $('.catalog-link-mobile').removeClass('stop');
            }
        } else {
            if (!$('.catalog-link-mobile').hasClass('stop')) {
                stopCarousel();
                $('.catalog-link-mobile').removeClass('started');
            }
        }
    });

    function startCarousel() {
        $('.catalog-link-mobile').owlCarousel({
            dots: false,
            nav: false,
            loop: true,
            autoplay: true,
            itemsDesktop: false,
            itemsDesktopSmall: false,
            itemsMobile: false,
            item: 2,
            responsive: {
                768: {
                    items: 3
                },
                1024: {
                    items: 4
                }
            }
        });
        $('.catalog-link-mobile').addClass('started');
    }

    function stopCarousel() {
        var owl = $('.catalog-link-mobile');
        owl.trigger('destroy.owl.carousel').removeClass('owl-carousel owl-loaded');
        owl.find('.owl-stage-outer').children().unwrap();
        $('.catalog-link-mobile').addClass('stop');
    }

    //header my account hover
    var timer;

    function debounce() {
        clearTimeout(timer);
        timer = setTimeout(function () {
            $(".customer-menu").hide();
        }, 450);
    }

    $("body").on('mouseenter', '.header.links', function( event ) {
        $('.customer-menu').show();
        clearTimeout(timer);
    }).on('mouseleave', '.header.links', function( event ) {
        debounce();
    });

    $('.customer-menu').mouseenter(function (e) {
        clearTimeout(timer);

    });
    $('.customer-menu').mouseleave(function (e) {
        debounce();
    });

    (function () {
        let isToggling = false;
        const toggleDelay = 300

        $(document).on('click touchend', '.nav-toggle', function (e) {
            e.preventDefault();
            e.stopPropagation();

            if (isToggling) return;
            isToggling = true;

            const html = $('html');

            if (html.hasClass('nav-open')) {
                html.removeClass('nav-open');
                $('.weltpixel_multistore').removeClass('open');
                setTimeout(() => {
                    html.removeClass('nav-before-open');
                    isToggling = false;
                }, toggleDelay);
            } else {
                html.addClass('nav-before-open');

                setTimeout(() => {
                    html.addClass('nav-open');
                    isToggling = false;
                }, toggleDelay);
            }
        });
    })();



})
;
define('WeltPixel_GoogleTagManager/js/weltpixel_gtm',[
    'jquery',
    ], function ($) {
    "use strict";

    return {
        trackPromotion: function(options) {
            if (options.enabled) {
                //$(document).ready(function() {
                $(window).on('on.window.load', function () {
                    var wpPersDl = options.persDataLayer;

                    /**  Track the promotion clicks   */
                    $('[data-track-promo-id]').click(function() {
                        var promoId = $(this).attr('data-track-promo-id'),
                            promoName = $(this).attr('data-track-promo-name'),
                            promoCreative = $(this).attr('data-track-promo-creative'),
                            promoPosition = $(this).attr('data-track-promo-position');

                        var promoObj = {
                            'id': promoId,
                            'name': promoName,
                            'creative': promoCreative,
                            'position': promoPosition
                        };

                        window.dataLayer.push({ecommerce: null});
                        window.dataLayer.push({
                            'event': 'promotionClick',
                            'ecommerce': {
                                'promoClick': {
                                    'promotions': [promoObj]
                                },'userID': window.userId
                            }
                        });

                        wpPersDl.setPromotionClick(promoObj);


                    });
                    /** Track the promotion views */
                    var promotionViews = [];
                    $('[data-track-promo-id]').each(function() {
                        var promoId = $(this).attr('data-track-promo-id'),
                            promoName = $(this).attr('data-track-promo-name'),
                            promoCreative = $(this).attr('data-track-promo-creative'),
                            promoPosition = $(this).attr('data-track-promo-position');

                        promotionViews.push({
                            'id': promoId,
                            'name': promoName,
                            'creative': promoCreative,
                            'position': promoPosition
                        });
                    });
                    if (promotionViews.length) {
                        window.dataLayer.push({ecommerce: null});
                        window.dataLayer.push({
                            'event': 'promotionView',
                            'ecommerce': {
                                'promoView': {
                                    'promotions': promotionViews,
                                    'userID': window.userId
                                }
                            }
                        });
                    }
                });
            }
        }
    };

});

define('Born_WeltPixelGtm/js/born_gtm',[
    'jquery',
], function ($) {
    "use strict";
    return {
        /**
         * push social links click data.
         */
        triggerSocialLinksClick: function () {
            $('.socia-media-links a.social').on('click', function () {
                var media = $(this).attr('id');
                media = media[0].toUpperCase() + media.slice(1);
                dataLayer.push({
                    'event': "followSocial",
                    'eventCategory': "Custom Event",
                    'eventAction': "Social Media Platform",
                    'eventLabel': media,
                    'userID': window.userId
                });
            });
        },

        /**
         * push store locator click data.
         */
        triggerStoreLocatorClick: function () {
            $('ul.list-store-container li.store-item').on('click', function () {
                var storeName = $(this).attr('title');
                dataLayer.push({
                    'event': "storeLocator",
                    'eventCategory': "Custom Event",
                    'eventAction': "Select Store Locator",
                    'selectedStoreName': storeName,
                    'userID': window.userId
                });
            });
        },

        /**
         * push promotion impressions data.
         */
        triggerPromotionImpressions: function () {
            var promotionViews = [];
            $('[data-track-promo-id]').each(function () {
                var promoId = $(this).attr('data-track-promo-id'),
                    promoName = $(this).attr('data-track-promo-name'),
                    promoCreative = $(this).attr('data-track-promo-name'),
                    promoPosition = $(this).attr('data-track-promo-position');
                promotionViews.push({
                    'name': promoName,
                    'id': promoId,
                    'creative': promoCreative,
                    'position': promoPosition
                });
            });
            if (promotionViews.length) {
                dataLayer.push({
                    'event': "promotionImpression",
                    'ecommerce': {
                        'promoView': {
                            'promotions': promotionViews
                        },
                        'userID': window.userId
                    }
                });
            }
        },

        /**
         * push promotion click data.
         */
        triggerPromotionClick: function () {
            $('[data-track-promo-id]').on('click', function () {
                var promoId = $(this).attr('data-track-promo-id'),
                    promoName = $(this).attr('data-track-promo-name'),
                    promoCreative = $(this).attr('data-track-promo-name'),
                    promoPosition = $(this).attr('data-track-promo-position');
                dataLayer.push({
                    'event': "promotionClick",
                    'ecommerce': {
                        'promoClick': {
                            'promotions': [{
                                'name': promoName,
                                'id': promoId,
                                'creative': promoCreative,
                                'position': promoPosition
                            }]
                        }
                    }
                });
            });
        },

        /**
         * push product impression.
         */
        triggerProductImpression: function () {
            var products = [], currencyCode = 'IDR';
            $('[data-track-info]').each(function () {
                if (!$(this).parent().parent().hasClass('cloned')) {
                    var productInfo = $(this).attr('data-track-info');
                    productInfo = $.parseJSON(productInfo);
                    currencyCode = productInfo.currencyCode;
                    delete productInfo.currencyCode;
                    products.push(productInfo);
                }
            });
            if (products.length) {
                dataLayer.push({
                    'event': 'productImpression',
                    'ecommerce': {
                        'currencyCode': currencyCode,
                        'impressions': products,
                        'userID': window.userId
                    }
                });
            }
        },

        /**
         * push product click data.
         */
        triggerProductClick: function () {
            $('[data-click-info]').on('click', function () {
                var productInfo = $(this).attr('data-click-info');
                productInfo = $.parseJSON(productInfo);
                delete productInfo.currencyCode;
                dataLayer.push({
                    'event': "productClick",
                    'ecommerce': {
                        'currencyCode': "IDR",
                        'click': {
                            'actionField': {'list': productInfo.list},
                            'products': [{
                                'name': productInfo.name,
                                'id': productInfo.sku,
                                'price': productInfo.price,
                                'brand': productInfo.brand,
                                'category': productInfo.category,
                                'position': productInfo.position,
                                'dimension22': productInfo.dimension22
                            }],
                            'userID': window.userId
                        }
                    }
                });
            });
        },

        /**
         * push brand click data.
         */
        triggerBrandClick: function () {
            $('.brands-slider-homepage .banner-item [data-brand]').on('click', function () {
                var brand = $(this).attr('data-brand');
                dataLayer.push({
                    'event': "brandClick",
                    'eventCategory': "Custom Event",
                    'eventAction': "Brand Menu Click",
                    'eventLabel': brand,
                    'userID': window.userId
                });
            });
        },

        /**
         * push order track data.
         *
         * @param orderId
         * @param status
         */
        triggerOrderTrack: function (orderId, status, userId) {
            dataLayer.push({
                'event': "trackingOrder",
                'eventCategory': "Custom Event",
                'eventAction': "Tracking Order Click",
                'eventLabel': status,
                'orderID': orderId,
                'userID': userId
            });
        },

        /**
         * push chart track data.
         */
        triggerChartTrack: function () {
            var productInfo = $.parseJSON(window.productInformation);
            $('.product-options-wrapper .open-chart').on('click', function () {
                dataLayer.push({
                    'event': "sizeChartClick",
                    'eventCategory': "Product Engagement",
                    'eventAction': "Size Chart Click",
                    'eventLabel': productInfo.url,
                    'productName': productInfo.name,
                    'productId': productInfo.id,
                    'productSku': productInfo.sku,
                    'productBrand': productInfo.brand,
                    'productPrice': productInfo.price,
                    'productCategory': productInfo.category,
                    'userID': window.userId
                });
            });
        },

        /**
         * push social share track data.
         */
        triggerSocialShareTrack: function () {
            var productInfo = $.parseJSON(window.productInformation);
            //$('.product-share-this .st_facebook_large, .product-share-this .st_twitter_large').on('click', function () {
            $('.social-sharing a').on('click', function () {
                var platform = $(this).attr('title');
                dataLayer.push({
                    'event': "productShare",
                    'eventCategory': "Product Engagement",
                    'eventAction': "Product Share",
                    'eventLabel': productInfo.url,
                    'productName': productInfo.name,
                    'productId': productInfo.dimension22,
                    'productSku': productInfo.id,
                    'productBrand': productInfo.brand,
                    'productPrice': productInfo.price,
                    'productCategory': productInfo.category,
                    'platform': platform,
                    'userID': window.userId
                });
            });
        },

        /**
         * push article track data.
         *
         * @param article
         */
        triggerArticleTrack: function (article) {
            var article = $.parseJSON(article);
            if (article.type !== 'non-article') {
                dataLayer.push({
                    'event': "articleEngagement",
                    'eventCategory': "Article Engagement",
                    'eventAction': "Article View",
                    'eventLabel': article.url,
                    'articleTitle': article.title,
                    'articleSource': article.source,
                    'articlePosition': article.position,
                    'articleType': article.type,
                    'articleCategory': article.category,
                    'articleAuthor': article.author,
                    'articlePublishedDate': article.published,
                    'articleLength': article.length,
                    'userID': window.userId
                });
            }
        },

        /**
         * trigger remarketing tags.
         * @param remarketingInfo
         */
        triggerRemarketingTag: function (remarketingInfo) {
            console.log($.parseJSON(remarketingInfo));
            dataLayer.push($.parseJSON(remarketingInfo) );
        }
    };
});

/*!
 * 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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/resizable',[
            "jquery",
            "./mouse",
            "../disable-selection",
            "../plugin",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.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;
                }
            }
        }

    } );

    return $.ui.resizable;

} );

/*!
 * 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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/dialog',[
            "jquery",
            "./button",
            "./draggable",
            "./mouse",
            "./resizable",
            "../focusable",
            "../keycode",
            "../position",
            "../safe-active-element",
            "../safe-blur",
            "../tabbable",
            "../unique-id",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    $.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 );
            }
        } );
    }

    return $.ui.dialog;

} );

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

// jscs:disable requireDotNotation

define('Magento_ReCaptchaWebapiUi/js/jquery-mixin',[
    'mage/utils/wrapper'
], function (wrapper) {
    'use strict';

    return function (jQuery) {
        jQuery.ajax = wrapper.wrapSuper(jQuery.ajax, function () {
            //Moving ReCaptcha value from payload to the header for requests to web API
            var settings,
                payload;

            if (arguments.length !== 0) {
                settings = arguments.length === 1 ? arguments[0] : arguments[1];
            }

            if (settings && settings.hasOwnProperty('data')) {
                //The request has a body, trying to parse JSON data
                try {
                    payload = JSON.parse(settings.data);
                } catch (e) {
                    //Not JSON
                }
            }

            if (payload && payload.hasOwnProperty('xReCaptchaValue')) {
                if (!settings.hasOwnProperty('headers')) {
                    settings.headers = {};
                }
                settings.headers['X-ReCaptcha'] = payload.xReCaptchaValue;
                delete payload['xReCaptchaValue'];
                settings.data = JSON.stringify(payload);
            }

            return this._super.apply(this, arguments);
        });

        return jQuery;
    };
});

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

define('Magento_PaypalCaptcha/js/view/checkout/defaultCaptcha-mixin',[
    'Magento_PaypalCaptcha/js/model/skipRefreshCaptcha'
], function (skipRefreshCaptcha) {
    'use strict';

    var defaultCaptchaMixin = {
        /**
         * @override
         */
        refresh: function () {
            if (!skipRefreshCaptcha.skip()) {
                this._super();
            } else {
                skipRefreshCaptcha.skip(false);
            }
        }
    };

    return function (defaultCaptcha) {
        return defaultCaptcha.extend(defaultCaptchaMixin);
    };
});

/**
 * 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');
});


define('text!Magento_Ui/template/messages.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<div data-role="checkout-messages" class="messages" data-bind="visible: isVisible(), click: removeAll">\n    <!-- ko foreach: messageContainer.getErrorMessages() -->\n    <div aria-atomic="true" role="alert" class="message message-error error">\n        <div data-ui-id="checkout-cart-validationmessages-message-error" data-bind="text: $data"></div>\n    </div>\n    <!--/ko-->\n    <!-- ko foreach: messageContainer.getSuccessMessages() -->\n    <div aria-atomic="true" role="alert" class="message message-success success">\n        <div data-ui-id="checkout-cart-validationmessages-message-success" data-bind="text: $data"></div>\n    </div>\n    <!--/ko-->\n</div>\n';});

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

//>>label: Blind Effect
//>>group: Effects
//>>description: Blinds the element.
//>>docs: http://api.jqueryui.com/blind-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "blind", "hide", function( options, done ) {
        var map = {
                up: [ "bottom", "top" ],
                vertical: [ "bottom", "top" ],
                down: [ "top", "bottom" ],
                left: [ "right", "left" ],
                horizontal: [ "right", "left" ],
                right: [ "left", "right" ]
            },
            element = $( this ),
            direction = options.direction || "up",
            start = element.cssClip(),
            animate = { clip: $.extend( {}, start ) },
            placeholder = $.effects.createPlaceholder( element );

        animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];

        if ( options.mode === "show" ) {
            element.cssClip( animate.clip );
            if ( placeholder ) {
                placeholder.css( $.effects.clipToBox( animate ) );
            }

            animate.clip = start;
        }

        if ( placeholder ) {
            placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
        }

        element.animate( animate, {
            queue: false,
            duration: options.duration,
            easing: options.easing,
            complete: done
        } );
    } );

} );

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

/**
 * @api
 */
define('Magento_Ui/js/view/messages',[
    'ko',
    'jquery',
    'uiComponent',
    '../model/messageList',
    'jquery-ui-modules/effect-blind'
], function (ko, $, Component, globalMessages) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Magento_Ui/messages',
            selector: '[data-role=checkout-messages]',
            isHidden: false,
            hideTimeout: 5000,
            hideSpeed: 500,
            listens: {
                isHidden: 'onHiddenChange'
            }
        },

        /** @inheritdoc */
        initialize: function (config, messageContainer) {
            this._super()
                .initObservable();

            this.messageContainer = messageContainer || config.messageContainer || globalMessages;

            return this;
        },

        /** @inheritdoc */
        initObservable: function () {
            this._super()
                .observe('isHidden');

            return this;
        },

        /**
         * Checks visibility.
         *
         * @return {Boolean}
         */
        isVisible: function () {
            return this.isHidden(this.messageContainer.hasMessages());
        },

        /**
         * Remove all messages.
         */
        removeAll: function () {
            this.messageContainer.clear();
        },

        /**
         * @param {Boolean} isHidden
         */
        onHiddenChange: function (isHidden) {
            // Hide message block if needed
            if (isHidden) {
                setTimeout(function () {
                    $(this.selector).hide('blind', {}, this.hideSpeed);
                }.bind(this), this.hideTimeout);
            }
        }
    });
});

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

define('Magento_Ui/js/block-loader',[
    'ko',
    'jquery',
    'Magento_Ui/js/lib/knockout/template/loader',
    'mage/template'
], function (ko, $, templateLoader, template) {
    'use strict';

    var blockLoaderTemplatePath = 'ui/block-loader',
        blockContentLoadingClass = '_block-content-loading',
        blockLoader,
        blockLoaderClass,
        blockLoaderElement = $.Deferred(),
        loaderImageHref = $.Deferred();

    templateLoader.loadTemplate(blockLoaderTemplatePath).done(function (blockLoaderTemplate) {
        loaderImageHref.done(function (loaderHref) {
            blockLoader = template(blockLoaderTemplate.trim(), {
                loaderImageHref: loaderHref
            });
            blockLoader = $(blockLoader);
            blockLoaderClass = '.' + blockLoader.attr('class');
            blockLoaderElement.resolve();
        });
    });

    /**
     * Helper function to check if blockContentLoading class should be applied.
     * @param {Object} element
     * @returns {Boolean}
     */
    function isLoadingClassRequired(element) {
        var position = element.css('position');

        if (position === 'absolute' || position === 'fixed') {
            return false;
        }

        return true;
    }

    /**
     * Add loader to block.
     * @param {Object} element
     */
    function addBlockLoader(element) {
        element.find(':focus').trigger('blur');
        element.find('input:disabled, select:disabled').addClass('_disabled');
        element.find('input, select').prop('disabled', true);

        if (isLoadingClassRequired(element)) {
            element.addClass(blockContentLoadingClass);
        }
        element.append(blockLoader.clone());
    }

    /**
     * Remove loader from block.
     * @param {Object} element
     */
    function removeBlockLoader(element) {
        if (!element.has(blockLoaderClass).length) {
            return;
        }
        element.find(blockLoaderClass).remove();
        element.find('input:not("._disabled"), select:not("._disabled")').prop('disabled', false);
        element.find('input:disabled, select:disabled').removeClass('_disabled');
        element.removeClass(blockContentLoadingClass);
    }

    return function (loaderHref) {
        loaderImageHref.resolve(loaderHref);
        ko.bindingHandlers.blockLoader = {
            /**
             * Process loader for block
             * @param {String} element
             * @param {Boolean} displayBlockLoader
             */
            update: function (element, displayBlockLoader) {
                element = $(element);

                if (ko.unwrap(displayBlockLoader())) {
                    blockLoaderElement.done(addBlockLoader(element));
                } else {
                    blockLoaderElement.done(removeBlockLoader(element));
                }
            }
        };
    };
});

/**
 * 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.
 */
/** Creates datepicker binding and registers in to ko.bindingHandlers object */
define('Magento_Ui/js/lib/knockout/bindings/datepicker',[
    'ko',
    'underscore',
    'jquery',
    'mage/translate'
], function (ko, _, $, $t) {
    'use strict';

    var defaults = {
        dateFormat: 'mm\/dd\/yyyy',
        showsTime: false,
        timeFormat: null,
        buttonImage: null,
        buttonImageOnly: null,
        buttonText: $t('Select Date')
    };

    ko.bindingHandlers.datepicker = {
        /**
         * Initializes calendar widget on element and stores it's value to observable property.
         * Datepicker binding takes either observable property or object
         *  { storage: {ko.observable}, options: {Object} }.
         * For more info about options take a look at "mage/calendar" and jquery.ui.datepicker widget.
         * @param {HTMLElement} el - Element, that binding is applied to
         * @param {Function} valueAccessor - Function that returns value, passed to binding
         * @param {object} allBindings
         * @param {object} viewModel
         * @param {object} bindingContext
         */
        init: function (el, valueAccessor, allBindings, viewModel, bindingContext) {
            var config = valueAccessor(),
                observable,
                options = {};

            _.extend(options, defaults);

            if (typeof config === 'object') {
                observable = config.storage;
                _.extend(options, config.options);
            } else {
                observable = config;
            }

            require(['mage/calendar'], function () {
                $(el).calendar(options);

                ko.utils.registerEventHandler(el, 'change', function () {
                    observable(this.value);
                });
            });

            if (bindingContext.$data) {
                bindingContext.$data.value.subscribe(function (newVal) {
                    if (!newVal) {
                        $(el).val('');
                    }
                }, this);
            }


        },

        /**
         * Update calendar widget on element and stores it's value to observable property.
         * Datepicker binding takes either observable property or object
         *  { storage: {ko.observable}, options: {Object} }.
         * @param {HTMLElement} element - Element, that binding is applied to
         * @param {Function} valueAccessor - Function that returns value, passed to binding
         */
        update: function (element, valueAccessor) {
            var config = valueAccessor(),
                $element = $(element),
                observable,
                options = {},
                newVal;

            _.extend(options, defaults);

            if (typeof config === 'object') {
                observable = config.storage;
                _.extend(options, config.options);
            } else {
                observable = config;
            }

            require(['moment', 'mage/utils/misc', 'mage/calendar'], function (moment, utils) {
                if (_.isEmpty(observable())) {
                    newVal = null;
                } else {
                    newVal = moment(
                        observable(),
                        utils.convertToMomentFormat(
                            options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '')
                        )
                    ).toDate();
                }

                if (!options.timeOnly) {
                    $element.datepicker('setDate', newVal);
                    $element.trigger('blur');
                }
            });
        }
    };
});

/**
 * 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/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/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');
});


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';});

/**
 * 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');
});

// 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','./resizable','./i18n','./scope','./range','./mage-init','./keyboard','./optgroup','./after-render','./autoselect','./datepicker','./outer_click','./fadeVisible','./collapsible','./staticChecked','./simple-checked','./bind-html','./tooltip','knockoutjs/knockout-repeat','knockoutjs/knockout-fast-foreach','./color-picker'],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 {
        resizable:      require('./resizable'),
        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'),
        datepicker:     require('./datepicker'),
        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'),
        colorPicker:    require('./color-picker')
    };
});

/**
 * 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);
        }
    });
});

/**
 * 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);
    ko.applyBindings();
});

/**
 * 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);
});

/*!
 * Stellar.js v0.6.2
 * http://markdalgleish.com/projects/stellar.js
 *
 * Copyright 2013, Mark Dalgleish
 * This content is released under the MIT license
 * http://markdalgleish.mit-license.org
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module depending on jQuery.
        define('WeltPixel_DesignElements/js/canvas/jquery.parallax',['jquery'], factory);
    } else {
        // No AMD. Register plugin with global jQuery object.
        factory(jQuery);
    }
}(function ($) {
    "use strict";

    var pluginName = 'stellar',
        defaults = {
            scrollProperty: 'scroll',
            positionProperty: 'position',
            horizontalScrolling: true,
            verticalScrolling: true,
            horizontalOffset: 0,
            verticalOffset: 0,
            responsive: false,
            parallaxBackgrounds: true,
            parallaxElements: true,
            hideDistantElements: true,
            hideElement: function($elem) { $elem.hide(); },
            showElement: function($elem) { $elem.show(); }
        },

        scrollProperty = {
            scroll: {
                getLeft: function($elem) { return $elem.scrollLeft(); },
                setLeft: function($elem, val) { $elem.scrollLeft(val); },

                getTop: function($elem) { return $elem.scrollTop();	},
                setTop: function($elem, val) { $elem.scrollTop(val); }
            },
            position: {
                getLeft: function($elem) { return parseInt($elem.css('left'), 10) * -1; },
                getTop: function($elem) { return parseInt($elem.css('top'), 10) * -1; }
            },
            margin: {
                getLeft: function($elem) { return parseInt($elem.css('margin-left'), 10) * -1; },
                getTop: function($elem) { return parseInt($elem.css('margin-top'), 10) * -1; }
            },
            transform: {
                getLeft: function($elem) {
                    var computedTransform = getComputedStyle($elem[0])[prefixedTransform];
                    return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[4], 10) * -1 : 0);
                },
                getTop: function($elem) {
                    var computedTransform = getComputedStyle($elem[0])[prefixedTransform];
                    return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[5], 10) * -1 : 0);
                }
            }
        },

        positionProperty = {
            position: {
                setLeft: function($elem, left) { $elem.css('left', left); },
                setTop: function($elem, top) { $elem.css('top', top); }
            },
            transform: {
                setPosition: function($elem, left, startingLeft, top, startingTop) {
                    $elem[0].style[prefixedTransform] = 'translate3d(' + (left - startingLeft) + 'px, ' + (top - startingTop) + 'px, 0)';
                }
            }
        },

        // Returns a function which adds a vendor prefix to any CSS property name
        vendorPrefix = (function() {
            var prefixes = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/,
                style = $('script')[0].style,
                prefix = '',
                prop;

            for (prop in style) {
                if (prefixes.test(prop)) {
                    prefix = prop.match(prefixes)[0];
                    break;
                }
            }

            if ('WebkitOpacity' in style) { prefix = 'Webkit'; }
            if ('KhtmlOpacity' in style) { prefix = 'Khtml'; }

            return function(property) {
                return prefix + (prefix.length > 0 ? property.charAt(0).toUpperCase() + property.slice(1) : property);
            };
        }()),

        prefixedTransform = vendorPrefix('transform'),

        supportsBackgroundPositionXY = $('<div />', { style: 'background:#fff' }).css('background-position-x') !== undefined,

        setBackgroundPosition = (supportsBackgroundPositionXY ?
                function($elem, x, y) {
                    $elem.css({
                        'background-position-x': x,
                        'background-position-y': y
                    });
                } :
                function($elem, x, y) {
                    $elem.css('background-position', x + ' ' + y);
                }
        ),

        getBackgroundPosition = (supportsBackgroundPositionXY ?
                function($elem) {
                    return [
                        $elem.css('background-position-x'),
                        $elem.css('background-position-y')
                    ];
                } :
                function($elem) {
                    return $elem.css('background-position').split(' ');
                }
        ),

        requestAnimFrame = (
            window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame     ||
            function(callback) {
                setTimeout(callback, 1000 / 60);
            }
        );

    function Plugin(element, options) {
        this.element = element;
        this.options = $.extend({}, defaults, options);

        this._defaults = defaults;
        this._name = pluginName;

        this.init();
    }

    Plugin.prototype = {
        init: function() {
            this.options.name = pluginName + '_' + Math.floor(Math.random() * 1e9);

            this._defineElements();
            this._defineGetters();
            this._defineSetters();
            this._handleWindowLoadAndResize();
            this._detectViewport();

            this.refresh({ firstLoad: true });

            if (this.options.scrollProperty === 'scroll') {
                this._handleScrollEvent();
            } else {
                this._startAnimationLoop();
            }
        },
        _defineElements: function() {
            if (this.element === document.body) this.element = window;
            this.$scrollElement = $(this.element);
            this.$element = (this.element === window ? $('body') : this.$scrollElement);
            this.$viewportElement = (this.options.viewportElement !== undefined ? $(this.options.viewportElement) : (this.$scrollElement[0] === window || this.options.scrollProperty === 'scroll' ? this.$scrollElement : this.$scrollElement.parent()) );
        },
        _defineGetters: function() {
            var self = this,
                scrollPropertyAdapter = scrollProperty[self.options.scrollProperty];

            this._getScrollLeft = function() {
                return scrollPropertyAdapter.getLeft(self.$scrollElement);
            };

            this._getScrollTop = function() {
                return scrollPropertyAdapter.getTop(self.$scrollElement);
            };
        },
        _defineSetters: function() {
            var self = this,
                scrollPropertyAdapter = scrollProperty[self.options.scrollProperty],
                positionPropertyAdapter = positionProperty[self.options.positionProperty],
                setScrollLeft = scrollPropertyAdapter.setLeft,
                setScrollTop = scrollPropertyAdapter.setTop;

            this._setScrollLeft = (typeof setScrollLeft === 'function' ? function(val) {
                setScrollLeft(self.$scrollElement, val);
            } : $.noop);

            this._setScrollTop = (typeof setScrollTop === 'function' ? function(val) {
                setScrollTop(self.$scrollElement, val);
            } : $.noop);

            this._setPosition = positionPropertyAdapter.setPosition ||
                function($elem, left, startingLeft, top, startingTop) {
                    if (self.options.horizontalScrolling) {
                        positionPropertyAdapter.setLeft($elem, left, startingLeft);
                    }

                    if (self.options.verticalScrolling) {
                        positionPropertyAdapter.setTop($elem, top, startingTop);
                    }
                };
        },
        _handleWindowLoadAndResize: function() {
            var self = this,
                $window = $(window);

            if (self.options.responsive) {
                $window.bind('load.' + this.name, function() {
                    self.refresh();
                });
            }

            $window.bind('resize.' + this.name, function() {
                self._detectViewport();

                if (self.options.responsive) {
                    self.refresh();
                }
            });
        },
        refresh: function(options) {
            var self = this,
                oldLeft = self._getScrollLeft(),
                oldTop = self._getScrollTop();

            if (!options || !options.firstLoad) {
                this._reset();
            }

            this._setScrollLeft(0);
            this._setScrollTop(0);

            this._setOffsets();
            this._findParticles();
            this._findBackgrounds();

            // Fix for WebKit background rendering bug
            if (options && options.firstLoad && /WebKit/.test(navigator.userAgent)) {
                $(window).on('load',function () {
                    var oldLeft = self._getScrollLeft(),
                        oldTop = self._getScrollTop();

                    self._setScrollLeft(oldLeft + 1);
                    self._setScrollTop(oldTop + 1);

                    self._setScrollLeft(oldLeft);
                    self._setScrollTop(oldTop);
                });
            }

            this._setScrollLeft(oldLeft);
            this._setScrollTop(oldTop);
        },
        _detectViewport: function() {
            var viewportOffsets = this.$viewportElement.offset(),
                hasOffsets = viewportOffsets !== null && viewportOffsets !== undefined;

            this.viewportWidth = this.$viewportElement.width();
            this.viewportHeight = this.$viewportElement.height();

            this.viewportOffsetTop = (hasOffsets ? viewportOffsets.top : 0);
            this.viewportOffsetLeft = (hasOffsets ? viewportOffsets.left : 0);
        },
        _findParticles: function() {
            var self = this,
                scrollLeft = this._getScrollLeft(),
                scrollTop = this._getScrollTop();

            if (this.particles !== undefined) {
                for (var i = this.particles.length - 1; i >= 0; i--) {
                    this.particles[i].$element.data('stellar-elementIsActive', undefined);
                }
            }

            this.particles = [];

            if (!this.options.parallaxElements) return;

            this.$element.find('[data-stellar-ratio]').each(function(i) {
                var $this = $(this),
                    horizontalOffset,
                    verticalOffset,
                    positionLeft,
                    positionTop,
                    marginLeft,
                    marginTop,
                    $offsetParent,
                    offsetLeft,
                    offsetTop,
                    parentOffsetLeft = 0,
                    parentOffsetTop = 0,
                    tempParentOffsetLeft = 0,
                    tempParentOffsetTop = 0;

                // Ensure this element isn't already part of another scrolling element
                if (!$this.data('stellar-elementIsActive')) {
                    $this.data('stellar-elementIsActive', this);
                } else if ($this.data('stellar-elementIsActive') !== this) {
                    return;
                }

                self.options.showElement($this);

                // Save/restore the original top and left CSS values in case we refresh the particles or destroy the instance
                if (!$this.data('stellar-startingLeft')) {
                    $this.data('stellar-startingLeft', $this.css('left'));
                    $this.data('stellar-startingTop', $this.css('top'));
                } else {
                    $this.css('left', $this.data('stellar-startingLeft'));
                    $this.css('top', $this.data('stellar-startingTop'));
                }

                positionLeft = $this.position().left;
                positionTop = $this.position().top;

                // Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8)
                marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10);
                marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10);

                offsetLeft = $this.offset().left - marginLeft;
                offsetTop = $this.offset().top - marginTop;

                // Calculate the offset parent
                $this.parents().each(function() {
                    var $this = $(this);

                    if ($this.data('stellar-offset-parent') === true) {
                        parentOffsetLeft = tempParentOffsetLeft;
                        parentOffsetTop = tempParentOffsetTop;
                        $offsetParent = $this;

                        return false;
                    } else {
                        tempParentOffsetLeft += $this.position().left;
                        tempParentOffsetTop += $this.position().top;
                    }
                });

                // Detect the offsets
                horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset));
                verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset));

                // Add our object to the particles collection
                self.particles.push({
                    $element: $this,
                    $offsetParent: $offsetParent,
                    isFixed: $this.css('position') === 'fixed',
                    horizontalOffset: horizontalOffset,
                    verticalOffset: verticalOffset,
                    startingPositionLeft: positionLeft,
                    startingPositionTop: positionTop,
                    startingOffsetLeft: offsetLeft,
                    startingOffsetTop: offsetTop,
                    parentOffsetLeft: parentOffsetLeft,
                    parentOffsetTop: parentOffsetTop,
                    stellarRatio: ($this.data('stellar-ratio') !== undefined ? $this.data('stellar-ratio') : 1),
                    width: $this.outerWidth(true),
                    height: $this.outerHeight(true),
                    isHidden: false
                });
            });
        },
        _findBackgrounds: function() {
            var self = this,
                scrollLeft = this._getScrollLeft(),
                scrollTop = this._getScrollTop(),
                $backgroundElements;

            this.backgrounds = [];

            if (!this.options.parallaxBackgrounds) return;

            $backgroundElements = this.$element.find('[data-stellar-background-ratio]');

            if (this.$element.data('stellar-background-ratio')) {
                $backgroundElements = $backgroundElements.add(this.$element);
            }

            $backgroundElements.each(function() {
                var $this = $(this),
                    backgroundPosition = getBackgroundPosition($this),
                    horizontalOffset,
                    verticalOffset,
                    positionLeft,
                    positionTop,
                    marginLeft,
                    marginTop,
                    offsetLeft,
                    offsetTop,
                    $offsetParent,
                    parentOffsetLeft = 0,
                    parentOffsetTop = 0,
                    tempParentOffsetLeft = 0,
                    tempParentOffsetTop = 0;

                // Ensure this element isn't already part of another scrolling element
                if (!$this.data('stellar-backgroundIsActive')) {
                    $this.data('stellar-backgroundIsActive', this);
                } else if ($this.data('stellar-backgroundIsActive') !== this) {
                    return;
                }

                // Save/restore the original top and left CSS values in case we destroy the instance
                if (!$this.data('stellar-backgroundStartingLeft')) {
                    $this.data('stellar-backgroundStartingLeft', backgroundPosition[0]);
                    $this.data('stellar-backgroundStartingTop', backgroundPosition[1]);
                } else {
                    setBackgroundPosition($this, $this.data('stellar-backgroundStartingLeft'), $this.data('stellar-backgroundStartingTop'));
                }

                // Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8)
                marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10);
                marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10);

                offsetLeft = $this.offset().left - marginLeft - scrollLeft;
                offsetTop = $this.offset().top - marginTop - scrollTop;

                // Calculate the offset parent
                $this.parents().each(function() {
                    var $this = $(this);

                    if ($this.data('stellar-offset-parent') === true) {
                        parentOffsetLeft = tempParentOffsetLeft;
                        parentOffsetTop = tempParentOffsetTop;
                        $offsetParent = $this;

                        return false;
                    } else {
                        tempParentOffsetLeft += $this.position().left;
                        tempParentOffsetTop += $this.position().top;
                    }
                });

                // Detect the offsets
                horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset));
                verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset));

                self.backgrounds.push({
                    $element: $this,
                    $offsetParent: $offsetParent,
                    isFixed: $this.css('background-attachment') === 'fixed',
                    horizontalOffset: horizontalOffset,
                    verticalOffset: verticalOffset,
                    startingValueLeft: backgroundPosition[0],
                    startingValueTop: backgroundPosition[1],
                    startingBackgroundPositionLeft: (isNaN(parseInt(backgroundPosition[0], 10)) ? 0 : parseInt(backgroundPosition[0], 10)),
                    startingBackgroundPositionTop: (isNaN(parseInt(backgroundPosition[1], 10)) ? 0 : parseInt(backgroundPosition[1], 10)),
                    startingPositionLeft: $this.position().left,
                    startingPositionTop: $this.position().top,
                    startingOffsetLeft: offsetLeft,
                    startingOffsetTop: offsetTop,
                    parentOffsetLeft: parentOffsetLeft,
                    parentOffsetTop: parentOffsetTop,
                    stellarRatio: ($this.data('stellar-background-ratio') === undefined ? 1 : $this.data('stellar-background-ratio'))
                });
            });
        },
        _reset: function() {
            var particle,
                startingPositionLeft,
                startingPositionTop,
                background,
                i;

            for (i = this.particles.length - 1; i >= 0; i--) {
                particle = this.particles[i];
                startingPositionLeft = particle.$element.data('stellar-startingLeft');
                startingPositionTop = particle.$element.data('stellar-startingTop');

                this._setPosition(particle.$element, startingPositionLeft, startingPositionLeft, startingPositionTop, startingPositionTop);

                this.options.showElement(particle.$element);

                particle.$element.data('stellar-startingLeft', null).data('stellar-elementIsActive', null).data('stellar-backgroundIsActive', null);
            }

            for (i = this.backgrounds.length - 1; i >= 0; i--) {
                background = this.backgrounds[i];

                background.$element.data('stellar-backgroundStartingLeft', null).data('stellar-backgroundStartingTop', null);

                setBackgroundPosition(background.$element, background.startingValueLeft, background.startingValueTop);
            }
        },
        destroy: function() {
            this._reset();

            this.$scrollElement.unbind('resize.' + this.name).unbind('scroll.' + this.name);
            this._animationLoop = $.noop;

            $(window).unbind('load.' + this.name).unbind('resize.' + this.name);
        },
        _setOffsets: function() {
            var self = this,
                $window = $(window);

            $window.unbind('resize.horizontal-' + this.name).unbind('resize.vertical-' + this.name);

            if (typeof this.options.horizontalOffset === 'function') {
                this.horizontalOffset = this.options.horizontalOffset();
                $window.bind('resize.horizontal-' + this.name, function() {
                    self.horizontalOffset = self.options.horizontalOffset();
                });
            } else {
                this.horizontalOffset = this.options.horizontalOffset;
            }

            if (typeof this.options.verticalOffset === 'function') {
                this.verticalOffset = this.options.verticalOffset();
                $window.bind('resize.vertical-' + this.name, function() {
                    self.verticalOffset = self.options.verticalOffset();
                });
            } else {
                this.verticalOffset = this.options.verticalOffset;
            }
        },
        _repositionElements: function() {
            var scrollLeft = this._getScrollLeft(),
                scrollTop = this._getScrollTop(),
                horizontalOffset,
                verticalOffset,
                particle,
                fixedRatioOffset,
                background,
                bgLeft,
                bgTop,
                isVisibleVertical = true,
                isVisibleHorizontal = true,
                newPositionLeft,
                newPositionTop,
                newOffsetLeft,
                newOffsetTop,
                i;

            // First check that the scroll position or container size has changed
            if (this.currentScrollLeft === scrollLeft && this.currentScrollTop === scrollTop && this.currentWidth === this.viewportWidth && this.currentHeight === this.viewportHeight) {
                return;
            } else {
                this.currentScrollLeft = scrollLeft;
                this.currentScrollTop = scrollTop;
                this.currentWidth = this.viewportWidth;
                this.currentHeight = this.viewportHeight;
            }

            // Reposition elements
            for (i = this.particles.length - 1; i >= 0; i--) {
                particle = this.particles[i];

                fixedRatioOffset = (particle.isFixed ? 1 : 0);

                // Calculate position, then calculate what the particle's new offset will be (for visibility check)
                if (this.options.horizontalScrolling) {
                    newPositionLeft = (scrollLeft + particle.horizontalOffset + this.viewportOffsetLeft + particle.startingPositionLeft - particle.startingOffsetLeft + particle.parentOffsetLeft) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionLeft;
                    newOffsetLeft = newPositionLeft - particle.startingPositionLeft + particle.startingOffsetLeft;
                } else {
                    newPositionLeft = particle.startingPositionLeft;
                    newOffsetLeft = particle.startingOffsetLeft;
                }

                if (this.options.verticalScrolling) {
                    newPositionTop = (scrollTop + particle.verticalOffset + this.viewportOffsetTop + particle.startingPositionTop - particle.startingOffsetTop + particle.parentOffsetTop) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionTop;
                    newOffsetTop = newPositionTop - particle.startingPositionTop + particle.startingOffsetTop;
                } else {
                    newPositionTop = particle.startingPositionTop;
                    newOffsetTop = particle.startingOffsetTop;
                }

                // Check visibility
                if (this.options.hideDistantElements) {
                    isVisibleHorizontal = !this.options.horizontalScrolling || newOffsetLeft + particle.width > (particle.isFixed ? 0 : scrollLeft) && newOffsetLeft < (particle.isFixed ? 0 : scrollLeft) + this.viewportWidth + this.viewportOffsetLeft;
                    isVisibleVertical = !this.options.verticalScrolling || newOffsetTop + particle.height > (particle.isFixed ? 0 : scrollTop) && newOffsetTop < (particle.isFixed ? 0 : scrollTop) + this.viewportHeight + this.viewportOffsetTop;
                }

                if (isVisibleHorizontal && isVisibleVertical) {
                    if (particle.isHidden) {
                        this.options.showElement(particle.$element);
                        particle.isHidden = false;
                    }

                    this._setPosition(particle.$element, newPositionLeft, particle.startingPositionLeft, newPositionTop, particle.startingPositionTop);
                } else {
                    if (!particle.isHidden) {
                        this.options.hideElement(particle.$element);
                        particle.isHidden = true;
                    }
                }
            }

            // Reposition backgrounds
            for (i = this.backgrounds.length - 1; i >= 0; i--) {
                background = this.backgrounds[i];

                fixedRatioOffset = (background.isFixed ? 0 : 1);
                bgLeft = (this.options.horizontalScrolling ? (scrollLeft + background.horizontalOffset - this.viewportOffsetLeft - background.startingOffsetLeft + background.parentOffsetLeft - background.startingBackgroundPositionLeft) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueLeft);
                bgTop = (this.options.verticalScrolling ? (scrollTop + background.verticalOffset - this.viewportOffsetTop - background.startingOffsetTop + background.parentOffsetTop - background.startingBackgroundPositionTop) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueTop);

                setBackgroundPosition(background.$element, bgLeft, bgTop);
            }
        },
        _handleScrollEvent: function() {
            var self = this,
                ticking = false;

            var update = function() {
                self._repositionElements();
                ticking = false;
            };

            var requestTick = function() {
                if (!ticking) {
                    requestAnimFrame(update);
                    ticking = true;
                }
            };

            this.$scrollElement.bind('scroll.' + this.name, requestTick);
            requestTick();
        },
        _startAnimationLoop: function() {
            var self = this;

            this._animationLoop = function() {
                requestAnimFrame(self._animationLoop);
                self._repositionElements();
            };
            this._animationLoop();
        }
    };

    $.fn[pluginName] = function (options) {
        var args = arguments;
        if (options === undefined || typeof options === 'object') {
            return this.each(function () {
                if (!$.data(this, 'plugin_' + pluginName)) {
                    $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
                }
            });
        } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
            return this.each(function () {
                var instance = $.data(this, 'plugin_' + pluginName);
                if (instance instanceof Plugin && typeof instance[options] === 'function') {
                    instance[options].apply(instance, Array.prototype.slice.call(args, 1));
                }
                if (options === 'destroy') {
                    $.data(this, 'plugin_' + pluginName, null);
                }
            });
        }
    };

    $[pluginName] = function(options) {
        var $window = $(window);
        return $window.stellar.apply($window, Array.prototype.slice.call(arguments, 0));
    };

    // Expose the scroll and position property function hashes so they can be extended
    $[pluginName].scrollProperty = scrollProperty;
    $[pluginName].positionProperty = positionProperty;

    // Expose the plugin class so it can be modified
    window.Stellar = Plugin;
}));

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

//>>label: Explode Effect
//>>group: Effects
/* eslint-disable max-len */
//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness.
/* eslint-enable max-len */
//>>docs: http://api.jqueryui.com/explode-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "explode", "hide", function( options, done ) {

        var i, j, left, top, mx, my,
            rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3,
            cells = rows,
            element = $( this ),
            mode = options.mode,
            show = mode === "show",

            // Show and then visibility:hidden the element before calculating offset
            offset = element.show().css( "visibility", "hidden" ).offset(),

            // Width and height of a piece
            width = Math.ceil( element.outerWidth() / cells ),
            height = Math.ceil( element.outerHeight() / rows ),
            pieces = [];

        // Children animate complete:
        function childComplete() {
            pieces.push( this );
            if ( pieces.length === rows * cells ) {
                animComplete();
            }
        }

        // Clone the element for each row and cell.
        for ( i = 0; i < rows; i++ ) { // ===>
            top = offset.top + i * height;
            my = i - ( rows - 1 ) / 2;

            for ( j = 0; j < cells; j++ ) { // |||
                left = offset.left + j * width;
                mx = j - ( cells - 1 ) / 2;

                // Create a clone of the now hidden main element that will be absolute positioned
                // within a wrapper div off the -left and -top equal to size of our pieces
                element
                    .clone()
                    .appendTo( "body" )
                    .wrap( "<div></div>" )
                    .css( {
                        position: "absolute",
                        visibility: "visible",
                        left: -j * width,
                        top: -i * height
                    } )

                    // Select the wrapper - make it overflow: hidden and absolute positioned based on
                    // where the original was located +left and +top equal to the size of pieces
                    .parent()
                    .addClass( "ui-effects-explode" )
                    .css( {
                        position: "absolute",
                        overflow: "hidden",
                        width: width,
                        height: height,
                        left: left + ( show ? mx * width : 0 ),
                        top: top + ( show ? my * height : 0 ),
                        opacity: show ? 0 : 1
                    } )
                    .animate( {
                        left: left + ( show ? 0 : mx * width ),
                        top: top + ( show ? 0 : my * height ),
                        opacity: show ? 1 : 0
                    }, options.duration || 500, options.easing, childComplete );
            }
        }

        function animComplete() {
            element.css( {
                visibility: "visible"
            } );
            $( pieces ).remove();
            done();
        }
    } );

} );

define('WeltPixel_DesignElements/js/designelements_default',['jquery', 'jRespond'], function ($) {
    "use strict";

    var $fullScreenEl = $('.full-screen'), $window = $(window), $body = $('body'), $verticalMiddleEl = $('.vertical-middle'), $header = $('.page-header'), $body = $('body'), $slider = $('#slider');

    var SEMICOLONDEFAULT = SEMICOLONDEFAULT || {};

    SEMICOLONDEFAULT.widget = {
        init: function () {
            SEMICOLONDEFAULT.widget.responsiveClasses();
            SEMICOLONDEFAULT.widget.dataResponsiveClasses();
            SEMICOLONDEFAULT.widget.dataResponsiveHeights();
            SEMICOLONDEFAULT.widget.verticalMiddle();
            SEMICOLONDEFAULT.widget.fullScreen();
        },
        responsiveClasses: function(){
            var jRes = jRespond([
                    {
                            label: 'smallest',
                            enter: 0,
                            exit: 479
                    },{
                            label: 'handheld',
                            enter: 480,
                            exit: 767
                    },{
                            label: 'tablet',
                            enter: 768,
                            exit: 991
                    },{
                            label: 'laptop',
                            enter: 992,
                            exit: 1199
                    },{
                            label: 'desktop',
                            enter: 1200,
                            exit: 10000
                    }
            ]);
            jRes.addFunc([
                    {
                            breakpoint: 'desktop',
                            enter: function() { $body.addClass('device-lg'); },
                            exit: function() { $body.removeClass('device-lg'); }
                    },{
                            breakpoint: 'laptop',
                            enter: function() { $body.addClass('device-md'); },
                            exit: function() { $body.removeClass('device-md'); }
                    },{
                            breakpoint: 'tablet',
                            enter: function() { $body.addClass('device-sm'); },
                            exit: function() { $body.removeClass('device-sm'); }
                    },{
                            breakpoint: 'handheld',
                            enter: function() { $body.addClass('device-xs'); },
                            exit: function() { $body.removeClass('device-xs'); }
                    },{
                            breakpoint: 'smallest',
                            enter: function() { $body.addClass('device-xxs'); },
                            exit: function() { $body.removeClass('device-xxs'); }
                    }
            ]);
        },
        dataResponsiveClasses: function(){
            var $dataClassXxs = $('[data-class-xxs]'),
                $dataClassXs = $('[data-class-xs]'),
                $dataClassSm = $('[data-class-sm]'),
                $dataClassMd = $('[data-class-md]'),
                $dataClassLg = $('[data-class-lg]');

            if( $dataClassXxs.length > 0 ) {
                $dataClassXxs.each( function(){
                    var element = $(this),
                        elementClass = element.attr('data-class-xxs'),
                        elementClassDelete = element.attr('data-class-xs') + ' ' + element.attr('data-class-sm') + ' ' + element.attr('data-class-md') + ' ' + element.attr('data-class-lg');

                    if( $body.hasClass('device-xxs') ) {
                        element.removeClass( elementClassDelete );
                        element.addClass( elementClass );
                    }
                });
            }

            if( $dataClassXs.length > 0 ) {
                $dataClassXs.each( function(){
                    var element = $(this),
                        elementClass = element.attr('data-class-xs'),
                        elementClassDelete = element.attr('data-class-xxs') + ' ' + element.attr('data-class-sm') + ' ' + element.attr('data-class-md') + ' ' + element.attr('data-class-lg');

                    if( $body.hasClass('device-xs') ) {
                        element.removeClass( elementClassDelete );
                        element.addClass( elementClass );
                    }
                });
            }

            if( $dataClassSm.length > 0 ) {
                $dataClassSm.each( function(){
                    var element = $(this),
                        elementClass = element.attr('data-class-sm'),
                        elementClassDelete = element.attr('data-class-xxs') + ' ' + element.attr('data-class-xs') + ' ' + element.attr('data-class-md') + ' ' + element.attr('data-class-lg');

                    if( $body.hasClass('device-sm') ) {
                        element.removeClass( elementClassDelete );
                        element.addClass( elementClass );
                    }
                });
            }

            if( $dataClassMd.length > 0 ) {
                $dataClassMd.each( function(){
                    var element = $(this),
                        elementClass = element.attr('data-class-md'),
                        elementClassDelete = element.attr('data-class-xxs') + ' ' + element.attr('data-class-xs') + ' ' + element.attr('data-class-sm') + ' ' + element.attr('data-class-lg');

                    if( $body.hasClass('device-md') ) {
                        element.removeClass( elementClassDelete );
                        element.addClass( elementClass );
                    }
                });
            }

            if( $dataClassLg.length > 0 ) {
                $dataClassLg.each( function(){
                    var element = $(this),
                        elementClass = element.attr('data-class-lg'),
                        elementClassDelete = element.attr('data-class-xxs') + ' ' + element.attr('data-class-xs') + ' ' + element.attr('data-class-sm') + ' ' + element.attr('data-class-md');

                    if( $body.hasClass('device-lg') ) {
                        element.removeClass( elementClassDelete );
                        element.addClass( elementClass );
                    }
                });
            }
        },
        dataResponsiveHeights: function(){
            var $dataHeightXxs = $('[data-height-xxs]'),
                $dataHeightXs = $('[data-height-xs]'),
                $dataHeightSm = $('[data-height-sm]'),
                $dataHeightMd = $('[data-height-md]'),
                $dataHeightLg = $('[data-height-lg]');

            if( $dataHeightXxs.length > 0 ) {
                $dataHeightXxs.each( function(){
                    var element = $(this),
                        elementHeight = element.attr('data-height-xxs');

                    if( $body.hasClass('device-xxs') ) {
                        if( elementHeight != '' ) { element.css( 'height', elementHeight ); }
                    }
                });
            }

            if( $dataHeightXs.length > 0 ) {
                $dataHeightXs.each( function(){
                    var element = $(this),
                        elementHeight = element.attr('data-height-xs');

                    if( $body.hasClass('device-xs') ) {
                        if( elementHeight != '' ) { element.css( 'height', elementHeight ); }
                    }
                });
            }

            if( $dataHeightSm.length > 0 ) {
                $dataHeightSm.each( function(){
                    var element = $(this),
                        elementHeight = element.attr('data-height-sm');

                    if( $body.hasClass('device-sm') ) {
                        if( elementHeight != '' ) { element.css( 'height', elementHeight ); }
                    }
                });
            }

            if( $dataHeightMd.length > 0 ) {
                $dataHeightMd.each( function(){
                    var element = $(this),
                        elementHeight = element.attr('data-height-md');

                    if( $body.hasClass('device-md') ) {
                        if( elementHeight != '' ) { element.css( 'height', elementHeight ); }
                    }
                });
            }

            if( $dataHeightLg.length > 0 ) {
                $dataHeightLg.each( function(){
                    var element = $(this),
                        elementHeight = element.attr('data-height-lg');

                    if( $body.hasClass('device-lg') ) {
                        if( elementHeight != '' ) { element.css( 'height', elementHeight ); }
                    }
                });
            }
        },
        verticalMiddle: function() {
            if ($verticalMiddleEl.length > 0) {
                $verticalMiddleEl.each(function () {
                    var element = $(this),
                        verticalMiddleH = element.outerHeight(),
                        headerHeight = $header.outerHeight();

                    if (element.parents('#slider').length > 0 && !element.hasClass('ignore-header')) {
                        if ($header.hasClass('transparent-header') && ( $body.hasClass('device-lg') || $body.hasClass('device-md') )) {
                            verticalMiddleH = verticalMiddleH - 70;
                            if ($slider.next('#header').length > 0) {
                                verticalMiddleH = verticalMiddleH + headerHeight;
                            }
                        }
                    }

                    if ($body.hasClass('device-xs') || $body.hasClass('device-xxs')) {
                        if (element.parents('.full-screen').length && !element.parents('.force-full-screen').length) {
                            if (element.children('.col-padding').length > 0) {
                                element.css({
                                    position: 'relative',
                                    top: '0',
                                    width: 'auto',
                                    marginTop: '0'
                                }).addClass('clearfix');
                            } else {
                                element.css({
                                    position: 'relative',
                                    top: '0',
                                    width: 'auto',
                                    marginTop: '0',
                                    paddingTop: '60px',
                                    paddingBottom: '60px'
                                }).addClass('clearfix');
                            }
                        } else {
                            element.css({
                                position: 'absolute',
                                top: '50%',
                                width: '100%',
                                paddingTop: '0',
                                paddingBottom: '0',
                                marginTop: -(verticalMiddleH / 2) + 'px'
                            });
                        }
                    } else {
                        element.css({
                            position: 'absolute',
                            top: '50%',
                            width: '100%',
                            paddingTop: '0',
                            paddingBottom: '0',
                            marginTop: -(verticalMiddleH / 2) + 'px'
                        });
                    }
                });
            }
        },

        fullScreen: function(){
            if( $fullScreenEl.length > 0 ) {
                $fullScreenEl.each( function(){
                    var element = $(this),
                        scrHeight = window.innerHeight ? window.innerHeight : $window.height(),
                        negativeHeight = element.attr('data-negative-height');

                    if( element.attr('id') == 'slider' ) {
                        var sliderHeightOff = $slider.offset().top;
                        scrHeight = scrHeight - sliderHeightOff;
                        if( element.hasClass('slider-parallax') ) {
                            var transformVal = element.css('transform'),
                                transformX = transformVal.match(/-?[\d\.]+/g);
                            if( !transformX ) { var transformXvalue = 0; } else { var transformXvalue = transformX[5]; }
                            scrHeight = ( ( window.innerHeight ? window.innerHeight : $window.height() ) + Number( transformXvalue ) ) - sliderHeightOff;
                        }
                        if( $('#slider.with-header').next('#header:not(.transparent-header)').length > 0 && ( $body.hasClass('device-lg') || $body.hasClass('device-md') ) ) {
                            var headerHeightOff = $header.outerHeight();
                            scrHeight = scrHeight - headerHeightOff;
                        }
                    }
                    if( element.parents('.full-screen').length > 0 ) { scrHeight = element.parents('.full-screen').height(); }

                    if( $body.hasClass('device-xs') || $body.hasClass('device-xxs') ) {
                        if( !element.hasClass('force-full-screen') ){ scrHeight = 'auto'; }
                    }

                    if( negativeHeight ){ scrHeight = scrHeight - Number(negativeHeight); }

                    element.css('height', scrHeight);
                    if( element.attr('id') == 'slider' && !element.hasClass('canvas-slider-grid') ) { if( element.has('.swiper-slide') ) { element.find('.swiper-slide').css('height', scrHeight); } }
                });
            }
        }

    };

    SEMICOLONDEFAULT.isMobile = {
        Android: function() {
            return navigator.userAgent.match(/Android/i);
        },
        BlackBerry: function() {
            return navigator.userAgent.match(/BlackBerry/i);
        },
        iOS: function() {
            return navigator.userAgent.match(/iPhone|iPad|iPod/i);
        },
        Opera: function() {
            return navigator.userAgent.match(/Opera Mini/i);
        },
        Windows: function() {
            return navigator.userAgent.match(/IEMobile/i);
        },
        any: function() {
            return (SEMICOLONDEFAULT.isMobile.Android() || SEMICOLONDEFAULT.isMobile.BlackBerry() || SEMICOLONDEFAULT.isMobile.iOS() || SEMICOLONDEFAULT.isMobile.Opera() || SEMICOLONDEFAULT.isMobile.Windows());
        }
    };

    return SEMICOLONDEFAULT;
});


define('text!ui/template/block-loader.html',[],function () { return '<!--\n/**\n * Copyright © Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<div data-role="loader" class="loading-mask" style="position: absolute;">\n    <div class="loader">\n        <img src="<%= loaderImageHref %>" alt="Loading..." title="Loading..." style="position: absolute;">\n    </div>\n</div>\n';});

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

//>>label: Transfer Effect
//>>group: Effects
//>>description: Displays a transfer effect from one element to another.
//>>docs: http://api.jqueryui.com/transfer-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    var effect;
    if ( $.uiBackCompat !== false ) {
        effect = $.effects.define( "transfer", function( options, done ) {
            $( this ).transfer( options, done );
        } );
    }
    return effect;

} );

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

//>>label: Progressbar
//>>group: Widgets
/* eslint-disable max-len */
//>>description: Displays a status indicator for loading state, standard percentage, and other progress indicators.
/* eslint-enable max-len */
//>>docs: http://api.jqueryui.com/progressbar/
//>>demos: http://jqueryui.com/progressbar/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/progressbar.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
    "use strict";

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

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

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

    return $.widget( "ui.progressbar", {
        version: "1.13.2",
        options: {
            classes: {
                "ui-progressbar": "ui-corner-all",
                "ui-progressbar-value": "ui-corner-left",
                "ui-progressbar-complete": "ui-corner-right"
            },
            max: 100,
            value: 0,

            change: null,
            complete: null
        },

        min: 0,

        _create: function() {

            // Constrain initial value
            this.oldValue = this.options.value = this._constrainedValue();

            this.element.attr( {

                // Only set static values; aria-valuenow and aria-valuemax are
                // set inside _refreshValue()
                role: "progressbar",
                "aria-valuemin": this.min
            } );
            this._addClass( "ui-progressbar", "ui-widget ui-widget-content" );

            this.valueDiv = $( "<div>" ).appendTo( this.element );
            this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" );
            this._refreshValue();
        },

        _destroy: function() {
            this.element.removeAttr( "role aria-valuemin aria-valuemax aria-valuenow" );

            this.valueDiv.remove();
        },

        value: function( newValue ) {
            if ( newValue === undefined ) {
                return this.options.value;
            }

            this.options.value = this._constrainedValue( newValue );
            this._refreshValue();
        },

        _constrainedValue: function( newValue ) {
            if ( newValue === undefined ) {
                newValue = this.options.value;
            }

            this.indeterminate = newValue === false;

            // Sanitize value
            if ( typeof newValue !== "number" ) {
                newValue = 0;
            }

            return this.indeterminate ? false :
                Math.min( this.options.max, Math.max( this.min, newValue ) );
        },

        _setOptions: function( options ) {

            // Ensure "value" option is set after other values (like max)
            var value = options.value;
            delete options.value;

            this._super( options );

            this.options.value = this._constrainedValue( value );
            this._refreshValue();
        },

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

                // Don't allow a max less than min
                value = Math.max( this.min, value );
            }
            this._super( key, value );
        },

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

            this.element.attr( "aria-disabled", value );
            this._toggleClass( null, "ui-state-disabled", !!value );
        },

        _percentage: function() {
            return this.indeterminate ?
                100 :
                100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
        },

        _refreshValue: function() {
            var value = this.options.value,
                percentage = this._percentage();

            this.valueDiv
                .toggle( this.indeterminate || value > this.min )
                .width( percentage.toFixed( 0 ) + "%" );

            this
                ._toggleClass( this.valueDiv, "ui-progressbar-complete", null,
                    value === this.options.max )
                ._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate );

            if ( this.indeterminate ) {
                this.element.removeAttr( "aria-valuenow" );
                if ( !this.overlayDiv ) {
                    this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv );
                    this._addClass( this.overlayDiv, "ui-progressbar-overlay" );
                }
            } else {
                this.element.attr( {
                    "aria-valuemax": this.options.max,
                    "aria-valuenow": value
                } );
                if ( this.overlayDiv ) {
                    this.overlayDiv.remove();
                    this.overlayDiv = null;
                }
            }

            if ( this.oldValue !== value ) {
                this.oldValue = value;
                this._trigger( "change" );
            }
            if ( value === this.options.max ) {
                this._trigger( "complete" );
            }
        }
    } );

} );

/* ========================================================================
 * Bootstrap: button.js v3.3.6
 * http://getbootstrap.com/javascript/#buttons
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */
define('WeltPixel_DesignElements/js/bootstrap/button',["jquery"], // Require jquery
    function($){

+function ($) {
    'use strict';

    // BUTTON PUBLIC CLASS DEFINITION
    // ==============================

    var Button = function (element, options) {
        this.$element  = $(element)
        this.options   = $.extend({}, Button.DEFAULTS, options)
        this.isLoading = false
    }

    Button.VERSION  = '3.3.6'

    Button.DEFAULTS = {
        loadingText: 'loading...'
    }

    Button.prototype.setState = function (state) {
        var d    = 'disabled'
        var $el  = this.$element
        var val  = $el.is('input') ? 'val' : 'html'
        var data = $el.data()

        state += 'Text'

        if (data.resetText == null) $el.data('resetText', $el[val]())

        // push to event loop to allow forms to submit
        setTimeout($.proxy(function () {
            $el[val](data[state] == null ? this.options[state] : data[state])

            if (state == 'loadingText') {
                this.isLoading = true
                $el.addClass(d).attr(d, d)
            } else if (this.isLoading) {
                this.isLoading = false
                $el.removeClass(d).removeAttr(d)
            }
        }, this), 0)
    }

    Button.prototype.toggle = function () {
        var changed = true
        var $parent = this.$element.closest('[data-toggle="buttons"]')

        if ($parent.length) {
            var $input = this.$element.find('input')
            if ($input.prop('type') == 'radio') {
                if ($input.prop('checked')) changed = false
                $parent.find('.active').removeClass('active')
                this.$element.addClass('active')
            } else if ($input.prop('type') == 'checkbox') {
                if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
                this.$element.toggleClass('active')
            }
            $input.prop('checked', this.$element.hasClass('active'))
            if (changed) $input.trigger('change')
        } else {
            this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
            this.$element.toggleClass('active')
        }
    }


    // BUTTON PLUGIN DEFINITION
    // ========================

    function Plugin(option) {
        return this.each(function () {
            var $this   = $(this)
            var data    = $this.data('bs.button')
            var options = typeof option == 'object' && option

            if (!data) $this.data('bs.button', (data = new Button(this, options)))

            if (option == 'toggle') data.toggle()
            else if (option) data.setState(option)
        })
    }

    var old = $.fn.button

    $.fn.button             = Plugin
    $.fn.button.Constructor = Button


    // BUTTON NO CONFLICT
    // ==================

    $.fn.button.noConflict = function () {
        $.fn.button = old
        return this
    }


    // BUTTON DATA-API
    // ===============

    $(document)
        .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
            var $btn = $(e.target)
            if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
            Plugin.call($btn, 'toggle')
            if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
        })
        .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
            $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
        })

}(jQuery);
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/form/adapter/buttons',[],function () {
    'use strict';

    return {
        'reset': '#reset',
        'save': '#save',
        'saveAndContinue': '#save_and_continue'
    };
});

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

/**
 * @api
 */
define('Magento_Ui/js/form/adapter',[
    'jquery',
    'underscore',
    'Magento_Ui/js/form/adapter/buttons'
], function ($, _, buttons) {
    'use strict';

    var selectorPrefix = '',
        eventPrefix;

    /**
     * Initialize listener.
     *
     * @param {Function} callback
     * @param {String} action
     */
    function initListener(callback, action) {
        var selector    = selectorPrefix ? selectorPrefix + ' ' + buttons[action] : buttons[action],
            elem        = $(selector)[0];

        if (!elem) {
            return;
        }

        if (elem.onclick) {
            elem.onclick = null;
        }

        $(elem).on('click' + eventPrefix, callback);
    }

    /**
     * Destroy listener.
     *
     * @param {String} action
     */
    function destroyListener(action) {
        var selector    = selectorPrefix ? selectorPrefix + ' ' + buttons[action] : buttons[action],
            elem        = $(selector)[0];

        if (!elem) {
            return;
        }

        if (elem.onclick) {
            elem.onclick = null;
        }

        $(elem).off('click' + eventPrefix);
    }

    return {

        /**
         * Attaches events handlers.
         *
         * @param {Object} handlers
         * @param {String} selectorPref
         * @param {String} eventPref
         */
        on: function (handlers, selectorPref, eventPref) {
            selectorPrefix = selectorPrefix || selectorPref;
            eventPrefix = eventPref;
            _.each(handlers, initListener);
            selectorPrefix = '';
        },

        /**
         * Removes events handlers.
         *
         * @param {Object} handlers
         * @param {String} eventPref
         */
        off: function (handlers, eventPref) {
            eventPrefix = eventPref;
            _.each(handlers, destroyListener);
        }
    };
});

define('WeltPixel_GoogleTagManager/js/weltpixel_persistentlayer',[
    'jquery', 'underscore', 'Magento_Ui/js/lib/core/storage/local', 'uiRegistry'
], function ($, _, localStorage, registry) {
    "use strict";

    var persistentLayer = {
        storageExpiryTime : 30, // specify in seconds;
        locStorage : registry.get('localStorage'),

        init: function(options) {
            this.storageExpiryTime = options.storageExpiryTime || this.storageExpiryTime;

            var persistentObject = {
                persist: {}
            };
            var pushToDatalayer = false;

            var promoClickObj = this.getPromotionClick();
            if (promoClickObj) {
                persistentObject.persist.persist_promotion = {};
                persistentObject.persist.persist_promotion.promotion = promoClickObj;
                pushToDatalayer = true;
            }

            if (pushToDatalayer) {
                window.dataLayer.push(persistentObject);
            }
        },

        setItem: function(key, value) {
            var storedValue = {
                expiryTime: new Date(),
                value: value
            };

            this.locStorage.set(key, storedValue);
        },

        getItem: function(key) {
            var storedValue = this.locStorage.get(key);
            if (typeof storedValue !== 'undefined') {

                if (this.isExpired(storedValue.expiryTime)) {
                    this.removeItem(key);
                    return false;
                }

                return storedValue.value;
            }

            return false;
        },

        removeItem: function(key) {
            this.locStorage.remove(key);
        },

        isExpired: function(date) {
            var currDate = new Date();
            var startDate = new Date(date);

            var difference = (currDate.getTime() - startDate.getTime()) / 1000;
            difference /= 60;
            difference = Math.abs(Math.round(difference));

            return difference > this.storageExpiryTime;
        },

        setPromotionClick: function(promoClick) {
            this.setItem('promo_click', promoClick);
        },

        getPromotionClick: function() {
            return this.getItem('promo_click');
        }
    };

    return persistentLayer;
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_PageBuilder/js/widget-initializer',[
    'underscore',
    'jquery',
    'mage/apply/main',
    'Magento_Ui/js/lib/view/utils/dom-observer'
], function (_, $, mage, domObserver) {
    'use strict';

    /**
     * Initializes components assigned to HTML elements.
     *
     *
     * @param {HTMLElement} el
     * @param {Array} data
     * @param {Object} breakpoints
     * @param {Object} currentViewport
     */
    function initializeWidget(el, data, breakpoints, currentViewport) {
        _.each(data, function (config, component) {
            config = config || {};
            config.breakpoints = breakpoints;
            config.currentViewport = currentViewport;
            mage.applyFor(el, config, component);
        });
    }

    return function (data, contextElement) {
        _.each(data.config, function (componentConfiguration, elementPath) {
            domObserver.get(
                elementPath,
                function (element) {
                    var $element = $(element);

                    if (contextElement) {
                        $element = $(contextElement).find(element);
                    }

                    if ($element.length) {
                        initializeWidget($element, componentConfiguration, data.breakpoints, data.currentViewport);
                    }
                }
            );
        });
    };
});

/**
 * 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);
            }
        }
    );
});

define('WeltPixel_DesignElements/js/animations',['jquery', 'jquery_important'], function ($) {
    "use strict";

    var SEMICOLONSANIMATIONS = SEMICOLONSANIMATIONS || {};

    SEMICOLONSANIMATIONS.widget = {
        init: function () {
            SEMICOLONSANIMATIONS.widget.animations();
        },

        animations: function(){

            var $body = $('body'), $dataAnimateEl = $('[data-animate]');
            if( $dataAnimateEl.length > 0 ){
                if( $body.hasClass('device-lg') || $body.hasClass('device-md') || $body.hasClass('device-sm') || $body.hasClass('wp-device-xs') ){
                    $dataAnimateEl.each(function(){
                        var element = $(this),
                            animationDelay = element.attr('data-delay'),
                            animationDelayTime = 0;
                        if( element.parents('.fslider.no-thumbs-animate').length > 0 ) { return true; }

                        if( animationDelay ) { animationDelayTime = Number( animationDelay ) + 500; } else { animationDelayTime = 500; }

                        if( !element.hasClass('animated') ) {
                            element.addClass('not-animated');
                            var elementAnimation = element.attr('data-animate');
                            element.appear(function () {
                                setTimeout(function() {
                                    element.removeClass('not-animated').addClass( elementAnimation + ' animated');
                                }, animationDelayTime);
                            },{accX: 0, accY: -120},'easeInCubic');
                        }
                    });
                }
            }
        }
    };

    return SEMICOLONSANIMATIONS;
});

/**
 * 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);
    };
});

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

//>>label: Clip Effect
//>>group: Effects
//>>description: Clips the element on and off like an old TV.
//>>docs: http://api.jqueryui.com/clip-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "clip", "hide", function( options, done ) {
        var start,
            animate = {},
            element = $( this ),
            direction = options.direction || "vertical",
            both = direction === "both",
            horizontal = both || direction === "horizontal",
            vertical = both || direction === "vertical";

        start = element.cssClip();
        animate.clip = {
            top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
            right: horizontal ? ( start.right - start.left ) / 2 : start.right,
            bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
            left: horizontal ? ( start.right - start.left ) / 2 : start.left
        };

        $.effects.createPlaceholder( element );

        if ( options.mode === "show" ) {
            element.cssClip( animate.clip );
            animate.clip = start;
        }

        element.animate( animate, {
            queue: false,
            duration: options.duration,
            easing: options.easing,
            complete: done
        } );

    } );

} );

define('Atome_MagentoPayment/js/global',["jquery"], function ($) {
    var mod = {};
    mod.init = function (pluginUrl) {
        $(document).ready(function () {
            const head = document.head || document.getElementsByTagName('head')[0];
            const script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = pluginUrl;
            head.appendChild(script);
        });
    }
    return mod;
});

/**
 * 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;
});

/*!
 * 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

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/accordion',[
            "jquery",
            "../version",
            "../keycode",
            "../unique-id",
            "../widget"
        ], factory );
    } else {

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

    return $.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 Effects Clip 1.13.2
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Clip Effect
//>>group: Effects
//>>description: Clips the element on and off like an old TV.
//>>docs: http://api.jqueryui.com/clip-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "clip", "hide", function( options, done ) {
        var start,
            animate = {},
            element = $( this ),
            direction = options.direction || "vertical",
            both = direction === "both",
            horizontal = both || direction === "horizontal",
            vertical = both || direction === "vertical";

        start = element.cssClip();
        animate.clip = {
            top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
            right: horizontal ? ( start.right - start.left ) / 2 : start.right,
            bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
            left: horizontal ? ( start.right - start.left ) / 2 : start.left
        };

        $.effects.createPlaceholder( element );

        if ( options.mode === "show" ) {
            element.cssClip( animate.clip );
            animate.clip = start;
        }

        element.animate( animate, {
            queue: false,
            duration: options.duration,
            easing: options.easing,
            complete: done
        } );

    } );

} );

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

//>>label: Fold Effect
//>>group: Effects
//>>description: Folds an element first horizontally and then vertically.
//>>docs: http://api.jqueryui.com/fold-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "fold", "hide", function( options, done ) {

        // Create element
        var element = $( this ),
            mode = options.mode,
            show = mode === "show",
            hide = mode === "hide",
            size = options.size || 15,
            percent = /([0-9]+)%/.exec( size ),
            horizFirst = !!options.horizFirst,
            ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ],
            duration = options.duration / 2,

            placeholder = $.effects.createPlaceholder( element ),

            start = element.cssClip(),
            animation1 = { clip: $.extend( {}, start ) },
            animation2 = { clip: $.extend( {}, start ) },

            distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ],

            queuelen = element.queue().length;

        if ( percent ) {
            size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
        }
        animation1.clip[ ref[ 0 ] ] = size;
        animation2.clip[ ref[ 0 ] ] = size;
        animation2.clip[ ref[ 1 ] ] = 0;

        if ( show ) {
            element.cssClip( animation2.clip );
            if ( placeholder ) {
                placeholder.css( $.effects.clipToBox( animation2 ) );
            }

            animation2.clip = start;
        }

        // Animate
        element
            .queue( function( next ) {
                if ( placeholder ) {
                    placeholder
                        .animate( $.effects.clipToBox( animation1 ), duration, options.easing )
                        .animate( $.effects.clipToBox( animation2 ), duration, options.easing );
                }

                next();
            } )
            .animate( animation1, duration, options.easing )
            .animate( animation2, duration, options.easing )
            .queue( done );

        $.effects.unshift( element, queuelen, 4 );
    } );

} );

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

//>>label: Pulsate Effect
//>>group: Effects
//>>description: Pulsates an element n times by changing the opacity to zero and back.
//>>docs: http://api.jqueryui.com/pulsate-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "pulsate", "show", function( options, done ) {
        var element = $( this ),
            mode = options.mode,
            show = mode === "show",
            hide = mode === "hide",
            showhide = show || hide,

            // Showing or hiding leaves off the "last" animation
            anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
            duration = options.duration / anims,
            animateTo = 0,
            i = 1,
            queuelen = element.queue().length;

        if ( show || !element.is( ":visible" ) ) {
            element.css( "opacity", 0 ).show();
            animateTo = 1;
        }

        // Anims - 1 opacity "toggles"
        for ( ; i < anims; i++ ) {
            element.animate( { opacity: animateTo }, duration, options.easing );
            animateTo = 1 - animateTo;
        }

        element.animate( { opacity: animateTo }, duration, options.easing );

        element.queue( done );

        $.effects.unshift( element, queuelen, anims + 1 );
    } );

} );

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

//>>label: Slide Effect
//>>group: Effects
//>>description: Slides an element in and out of the viewport.
//>>docs: http://api.jqueryui.com/slide-effect/
//>>demos: http://jqueryui.com/effect/

( function( factory ) {
    "use strict";

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

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

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

    return $.effects.define( "slide", "show", function( options, done ) {
        var startClip, startRef,
            element = $( this ),
            map = {
                up: [ "bottom", "top" ],
                down: [ "top", "bottom" ],
                left: [ "right", "left" ],
                right: [ "left", "right" ]
            },
            mode = options.mode,
            direction = options.direction || "left",
            ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
            positiveMotion = ( direction === "up" || direction === "left" ),
            distance = options.distance ||
                element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ),
            animation = {};

        $.effects.createPlaceholder( element );

        startClip = element.cssClip();
        startRef = element.position()[ ref ];

        // Define hide animation
        animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef;
        animation.clip = element.cssClip();
        animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ];

        // Reverse the animation if we're showing
        if ( mode === "show" ) {
            element.cssClip( animation.clip );
            element.css( ref, animation[ ref ] );
            animation.clip = startClip;
            animation[ ref ] = startRef;
        }

        // Actually animate
        element.animate( animation, {
            queue: false,
            duration: options.duration,
            easing: options.easing,
            complete: done
        } );
    } );

} );

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

//>>label: Selectable
//>>group: Interactions
//>>description: Allows groups of elements to be selected with the mouse.
//>>docs: http://api.jqueryui.com/selectable/
//>>demos: http://jqueryui.com/selectable/
//>>css.structure: ../../themes/base/selectable.css

( function( factory ) {
    "use strict";

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

        // AMD. Register as an anonymous module.
        define( 'jquery/ui-modules/widgets/selectable',[
            "jquery",
            "./mouse",
            "../version",
            "../widget"
        ], factory );
    } else {

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

    return $.widget( "ui.selectable", $.ui.mouse, {
        version: "1.13.2",
        options: {
            appendTo: "body",
            autoRefresh: true,
            distance: 0,
            filter: "*",
            tolerance: "touch",

            // Callbacks
            selected: null,
            selecting: null,
            start: null,
            stop: null,
            unselected: null,
            unselecting: null
        },
        _create: function() {
            var that = this;

            this._addClass( "ui-selectable" );

            this.dragged = false;

            // Cache selectee children based on filter
            this.refresh = function() {
                that.elementPos = $( that.element[ 0 ] ).offset();
                that.selectees = $( that.options.filter, that.element[ 0 ] );
                that._addClass( that.selectees, "ui-selectee" );
                that.selectees.each( function() {
                    var $this = $( this ),
                        selecteeOffset = $this.offset(),
                        pos = {
                            left: selecteeOffset.left - that.elementPos.left,
                            top: selecteeOffset.top - that.elementPos.top
                        };
                    $.data( this, "selectable-item", {
                        element: this,
                        $element: $this,
                        left: pos.left,
                        top: pos.top,
                        right: pos.left + $this.outerWidth(),
                        bottom: pos.top + $this.outerHeight(),
                        startselected: false,
                        selected: $this.hasClass( "ui-selected" ),
                        selecting: $this.hasClass( "ui-selecting" ),
                        unselecting: $this.hasClass( "ui-unselecting" )
                    } );
                } );
            };
            this.refresh();

            this._mouseInit();

            this.helper = $( "<div>" );
            this._addClass( this.helper, "ui-selectable-helper" );
        },

        _destroy: function() {
            this.selectees.removeData( "selectable-item" );
            this._mouseDestroy();
        },

        _mouseStart: function( event ) {
            var that = this,
                options = this.options;

            this.opos = [ event.pageX, event.pageY ];
            this.elementPos = $( this.element[ 0 ] ).offset();

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

            this.selectees = $( options.filter, this.element[ 0 ] );

            this._trigger( "start", event );

            $( options.appendTo ).append( this.helper );

            // position helper (lasso)
            this.helper.css( {
                "left": event.pageX,
                "top": event.pageY,
                "width": 0,
                "height": 0
            } );

            if ( options.autoRefresh ) {
                this.refresh();
            }

            this.selectees.filter( ".ui-selected" ).each( function() {
                var selectee = $.data( this, "selectable-item" );
                selectee.startselected = true;
                if ( !event.metaKey && !event.ctrlKey ) {
                    that._removeClass( selectee.$element, "ui-selected" );
                    selectee.selected = false;
                    that._addClass( selectee.$element, "ui-unselecting" );
                    selectee.unselecting = true;

                    // selectable UNSELECTING callback
                    that._trigger( "unselecting", event, {
                        unselecting: selectee.element
                    } );
                }
            } );

            $( event.target ).parents().addBack().each( function() {
                var doSelect,
                    selectee = $.data( this, "selectable-item" );
                if ( selectee ) {
                    doSelect = ( !event.metaKey && !event.ctrlKey ) ||
                        !selectee.$element.hasClass( "ui-selected" );
                    that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" )
                        ._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" );
                    selectee.unselecting = !doSelect;
                    selectee.selecting = doSelect;
                    selectee.selected = doSelect;

                    // selectable (UN)SELECTING callback
                    if ( doSelect ) {
                        that._trigger( "selecting", event, {
                            selecting: selectee.element
                        } );
                    } else {
                        that._trigger( "unselecting", event, {
                            unselecting: selectee.element
                        } );
                    }
                    return false;
                }
            } );

        },

        _mouseDrag: function( event ) {

            this.dragged = true;

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

            var tmp,
                that = this,
                options = this.options,
                x1 = this.opos[ 0 ],
                y1 = this.opos[ 1 ],
                x2 = event.pageX,
                y2 = event.pageY;

            if ( x1 > x2 ) {
                tmp = x2; x2 = x1; x1 = tmp;
            }
            if ( y1 > y2 ) {
                tmp = y2; y2 = y1; y1 = tmp;
            }
            this.helper.css( { left: x1, top: y1, width: x2 - x1, height: y2 - y1 } );

            this.selectees.each( function() {
                var selectee = $.data( this, "selectable-item" ),
                    hit = false,
                    offset = {};

                //prevent helper from being selected if appendTo: selectable
                if ( !selectee || selectee.element === that.element[ 0 ] ) {
                    return;
                }

                offset.left   = selectee.left   + that.elementPos.left;
                offset.right  = selectee.right  + that.elementPos.left;
                offset.top    = selectee.top    + that.elementPos.top;
                offset.bottom = selectee.bottom + that.elementPos.top;

                if ( options.tolerance === "touch" ) {
                    hit = ( !( offset.left > x2 || offset.right < x1 || offset.top > y2 ||
                        offset.bottom < y1 ) );
                } else if ( options.tolerance === "fit" ) {
                    hit = ( offset.left > x1 && offset.right < x2 && offset.top > y1 &&
                        offset.bottom < y2 );
                }

                if ( hit ) {

                    // SELECT
                    if ( selectee.selected ) {
                        that._removeClass( selectee.$element, "ui-selected" );
                        selectee.selected = false;
                    }
                    if ( selectee.unselecting ) {
                        that._removeClass( selectee.$element, "ui-unselecting" );
                        selectee.unselecting = false;
                    }
                    if ( !selectee.selecting ) {
                        that._addClass( selectee.$element, "ui-selecting" );
                        selectee.selecting = true;

                        // selectable SELECTING callback
                        that._trigger( "selecting", event, {
                            selecting: selectee.element
                        } );
                    }
                } else {

                    // UNSELECT
                    if ( selectee.selecting ) {
                        if ( ( event.metaKey || event.ctrlKey ) && selectee.startselected ) {
                            that._removeClass( selectee.$element, "ui-selecting" );
                            selectee.selecting = false;
                            that._addClass( selectee.$element, "ui-selected" );
                            selectee.selected = true;
                        } else {
                            that._removeClass( selectee.$element, "ui-selecting" );
                            selectee.selecting = false;
                            if ( selectee.startselected ) {
                                that._addClass( selectee.$element, "ui-unselecting" );
                                selectee.unselecting = true;
                            }

                            // selectable UNSELECTING callback
                            that._trigger( "unselecting", event, {
                                unselecting: selectee.element
                            } );
                        }
                    }
                    if ( selectee.selected ) {
                        if ( !event.metaKey && !event.ctrlKey && !selectee.startselected ) {
                            that._removeClass( selectee.$element, "ui-selected" );
                            selectee.selected = false;

                            that._addClass( selectee.$element, "ui-unselecting" );
                            selectee.unselecting = true;

                            // selectable UNSELECTING callback
                            that._trigger( "unselecting", event, {
                                unselecting: selectee.element
                            } );
                        }
                    }
                }
            } );

            return false;
        },

        _mouseStop: function( event ) {
            var that = this;

            this.dragged = false;

            $( ".ui-unselecting", this.element[ 0 ] ).each( function() {
                var selectee = $.data( this, "selectable-item" );
                that._removeClass( selectee.$element, "ui-unselecting" );
                selectee.unselecting = false;
                selectee.startselected = false;
                that._trigger( "unselected", event, {
                    unselected: selectee.element
                } );
            } );
            $( ".ui-selecting", this.element[ 0 ] ).each( function() {
                var selectee = $.data( this, "selectable-item" );
                that._removeClass( selectee.$element, "ui-selecting" )
                    ._addClass( selectee.$element, "ui-selected" );
                selectee.selecting = false;
                selectee.selected = true;
                selectee.startselected = true;
                that._trigger( "selected", event, {
                    selected: selectee.element
                } );
            } );
            this._trigger( "stop", event );

            this.helper.remove();

            return false;
        }

    } );

} );

// Import every plugin under the sun. Bad for performance,
// but prevents the store from breaking in situations
// where a dependency was missed during the migration from
// a monolith build of jQueryUI to a modular one

define('jquery/compat',[
    'jquery-ui-modules/core',
    'jquery-ui-modules/accordion',
    'jquery-ui-modules/autocomplete',
    'jquery-ui-modules/button',
    'jquery-ui-modules/datepicker',
    'jquery-ui-modules/dialog',
    'jquery-ui-modules/draggable',
    'jquery-ui-modules/droppable',
    'jquery-ui-modules/effect-blind',
    'jquery-ui-modules/effect-bounce',
    'jquery-ui-modules/effect-clip',
    'jquery-ui-modules/effect-drop',
    'jquery-ui-modules/effect-explode',
    'jquery-ui-modules/effect-fade',
    'jquery-ui-modules/effect-fold',
    'jquery-ui-modules/effect-highlight',
    'jquery-ui-modules/effect-scale',
    'jquery-ui-modules/effect-pulsate',
    'jquery-ui-modules/effect-shake',
    'jquery-ui-modules/effect-slide',
    'jquery-ui-modules/effect-transfer',
    'jquery-ui-modules/effect',
    'jquery-ui-modules/menu',
    'jquery-ui-modules/mouse',
    'jquery-ui-modules/position',
    'jquery-ui-modules/progressbar',
    'jquery-ui-modules/resizable',
    'jquery-ui-modules/selectable',
    'jquery-ui-modules/slider',
    'jquery-ui-modules/sortable',
    'jquery-ui-modules/spinner',
    'jquery-ui-modules/tabs',
    'jquery-ui-modules/timepicker',
    'jquery-ui-modules/tooltip',
    'jquery-ui-modules/widget'
], function() {
    console.warn(
        'Fallback to JQueryUI Compat activated. ' +
        'Your store is missing a dependency for a ' +
        'jQueryUI widget. Identifying and addressing the dependency ' +
        'will drastically improve the performance of your site.'
    );
});

/**
 * 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(data, currentData));
            }
        },

        /**
         * 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);

                _.each(currentDataIds, function (id) {
                    if (_.lastIndexOf(sentDataIds, id) === -1) {
                        return false;
                    }
                });

                return true;
            }

            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/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.
 */

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 © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define('Magento_Checkout/js/checkout-loader',[
    'rjsResolver'
], function (resolver) {
    'use strict';

    /**
     * Removes provided loader element from DOM.
     *
     * @param {HTMLElement} $loader - Loader DOM element.
     */
    function hideLoader($loader) {
        $loader.parentNode.removeChild($loader);
    }

    /**
     * Initializes assets loading process listener.
     *
     * @param {Object} config - Optional configuration
     * @param {HTMLElement} $loader - Loader DOM element.
     */
    function init(config, $loader) {
        resolver(hideLoader.bind(null, $loader));
    }

    return init;
});

!function(t,e){"function"==typeof define&&define.amd?define('WeltPixel_DesignElements/js/canvas/jquery.transition',["jquery"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(t.jQuery)}(this,function(t){function e(t){if(t in p.style)return t;for(var e=["Moz","Webkit","O","ms"],n=t.charAt(0).toUpperCase()+t.substr(1),i=0;i<e.length;++i){var r=e[i]+n;if(r in p.style)return r}}function n(){return p.style[d.transform]="",p.style[d.transform]="rotateY(90deg)",""!==p.style[d.transform]}function i(t){return"string"==typeof t&&this.parse(t),this}function r(t,e,n){e===!0?t.queue(n):e?t.queue(e,n):t.each(function(){n.call(this)})}function s(e){var n=[];return t.each(e,function(e){e=t.camelCase(e),e=t.transit.propertyMap[e]||t.cssProps[e]||e,e=u(e),d[e]&&(e=u(d[e])),-1===t.inArray(e,n)&&n.push(e)}),n}function a(e,n,i,r){var a=s(e);t.cssEase[i]&&(i=t.cssEase[i]);var o=""+f(n)+" "+i;parseInt(r,10)>0&&(o+=" "+f(r));var u=[];return t.each(a,function(t,e){u.push(e+" "+o)}),u.join(", ")}function o(e,n){n||(t.cssNumber[e]=!0),t.transit.propertyMap[e]=d.transform,t.cssHooks[e]={get:function(n){var i=t(n).css("transit:transform");return i.get(e)},set:function(n,i){var r=t(n).css("transit:transform");r.setFromString(e,i),t(n).css({"transit:transform":r})}}}function u(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}function c(t,e){return"string"!=typeof t||t.match(/^[\-0-9\.]+$/)?""+t+e:t}function f(e){var n=e;return"string"!=typeof n||n.match(/^[\-0-9\.]+/)||(n=t.fx.speeds[n]||t.fx.speeds._default),c(n,"ms")}t.transit={version:"0.9.12",propertyMap:{marginLeft:"margin",marginRight:"margin",marginBottom:"margin",marginTop:"margin",paddingLeft:"padding",paddingRight:"padding",paddingBottom:"padding",paddingTop:"padding"},enabled:!0,useTransitionEnd:!1};var p=document.createElement("div"),d={},l=navigator.userAgent.toLowerCase().indexOf("chrome")>-1;d.transition=e("transition"),d.transitionDelay=e("transitionDelay"),d.transform=e("transform"),d.transformOrigin=e("transformOrigin"),d.filter=e("Filter"),d.transform3d=n();var h={transition:"transitionend",MozTransition:"transitionend",OTransition:"oTransitionEnd",WebkitTransition:"webkitTransitionEnd",msTransition:"MSTransitionEnd"},b=d.transitionEnd=h[d.transition]||null;for(var y in d)d.hasOwnProperty(y)&&"undefined"==typeof t.support[y]&&(t.support[y]=d[y]);return p=null,t.cssEase={_default:"ease","in":"ease-in",out:"ease-out","in-out":"ease-in-out",snap:"cubic-bezier(0,1,.5,1)",easeInCubic:"cubic-bezier(.550,.055,.675,.190)",easeOutCubic:"cubic-bezier(.215,.61,.355,1)",easeInOutCubic:"cubic-bezier(.645,.045,.355,1)",easeInCirc:"cubic-bezier(.6,.04,.98,.335)",easeOutCirc:"cubic-bezier(.075,.82,.165,1)",easeInOutCirc:"cubic-bezier(.785,.135,.15,.86)",easeInExpo:"cubic-bezier(.95,.05,.795,.035)",easeOutExpo:"cubic-bezier(.19,1,.22,1)",easeInOutExpo:"cubic-bezier(1,0,0,1)",easeInQuad:"cubic-bezier(.55,.085,.68,.53)",easeOutQuad:"cubic-bezier(.25,.46,.45,.94)",easeInOutQuad:"cubic-bezier(.455,.03,.515,.955)",easeInQuart:"cubic-bezier(.895,.03,.685,.22)",easeOutQuart:"cubic-bezier(.165,.84,.44,1)",easeInOutQuart:"cubic-bezier(.77,0,.175,1)",easeInQuint:"cubic-bezier(.755,.05,.855,.06)",easeOutQuint:"cubic-bezier(.23,1,.32,1)",easeInOutQuint:"cubic-bezier(.86,0,.07,1)",easeInSine:"cubic-bezier(.47,0,.745,.715)",easeOutSine:"cubic-bezier(.39,.575,.565,1)",easeInOutSine:"cubic-bezier(.445,.05,.55,.95)",easeInBack:"cubic-bezier(.6,-.28,.735,.045)",easeOutBack:"cubic-bezier(.175, .885,.32,1.275)",easeInOutBack:"cubic-bezier(.68,-.55,.265,1.55)"},t.cssHooks["transit:transform"]={get:function(e){return t(e).data("transform")||new i},set:function(e,n){var r=n;r instanceof i||(r=new i(r)),e.style[d.transform]="WebkitTransform"!==d.transform||l?r.toString():r.toString(!0),t(e).data("transform",r)}},t.cssHooks.transform={set:t.cssHooks["transit:transform"].set},t.cssHooks.filter={get:function(t){return t.style[d.filter]},set:function(t,e){t.style[d.filter]=e}},t.fn.jquery<"1.8"&&(t.cssHooks.transformOrigin={get:function(t){return t.style[d.transformOrigin]},set:function(t,e){t.style[d.transformOrigin]=e}},t.cssHooks.transition={get:function(t){return t.style[d.transition]},set:function(t,e){t.style[d.transition]=e}}),o("scale"),o("scaleX"),o("scaleY"),o("translate"),o("rotate"),o("rotateX"),o("rotateY"),o("rotate3d"),o("perspective"),o("skewX"),o("skewY"),o("x",!0),o("y",!0),i.prototype={setFromString:function(t,e){var n="string"==typeof e?e.split(","):e.constructor===Array?e:[e];n.unshift(t),i.prototype.set.apply(this,n)},set:function(t){var e=Array.prototype.slice.apply(arguments,[1]);this.setter[t]?this.setter[t].apply(this,e):this[t]=e.join(",")},get:function(t){return this.getter[t]?this.getter[t].apply(this):this[t]||0},setter:{rotate:function(t){this.rotate=c(t,"deg")},rotateX:function(t){this.rotateX=c(t,"deg")},rotateY:function(t){this.rotateY=c(t,"deg")},scale:function(t,e){void 0===e&&(e=t),this.scale=t+","+e},skewX:function(t){this.skewX=c(t,"deg")},skewY:function(t){this.skewY=c(t,"deg")},perspective:function(t){this.perspective=c(t,"px")},x:function(t){this.set("translate",t,null)},y:function(t){this.set("translate",null,t)},translate:function(t,e){void 0===this._translateX&&(this._translateX=0),void 0===this._translateY&&(this._translateY=0),null!==t&&void 0!==t&&(this._translateX=c(t,"px")),null!==e&&void 0!==e&&(this._translateY=c(e,"px")),this.translate=this._translateX+","+this._translateY}},getter:{x:function(){return this._translateX||0},y:function(){return this._translateY||0},scale:function(){var t=(this.scale||"1,1").split(",");return t[0]&&(t[0]=parseFloat(t[0])),t[1]&&(t[1]=parseFloat(t[1])),t[0]===t[1]?t[0]:t},rotate3d:function(){for(var t=(this.rotate3d||"0,0,0,0deg").split(","),e=0;3>=e;++e)t[e]&&(t[e]=parseFloat(t[e]));return t[3]&&(t[3]=c(t[3],"deg")),t}},parse:function(t){var e=this;t.replace(/([a-zA-Z0-9]+)\((.*?)\)/g,function(t,n,i){e.setFromString(n,i)})},toString:function(t){var e=[];for(var n in this)if(this.hasOwnProperty(n)){if(!d.transform3d&&("rotateX"===n||"rotateY"===n||"perspective"===n||"transformOrigin"===n))continue;"_"!==n[0]&&e.push(t&&"scale"===n?n+"3d("+this[n]+",1)":t&&"translate"===n?n+"3d("+this[n]+",0)":n+"("+this[n]+")")}return e.join(" ")}},t.fn.transition=t.fn.transit=function(e,n,i,s){var o=this,u=0,c=!0,p=t.extend(!0,{},e);"function"==typeof n&&(s=n,n=void 0),"object"==typeof n&&(i=n.easing,u=n.delay||0,c="undefined"==typeof n.queue?!0:n.queue,s=n.complete,n=n.duration),"function"==typeof i&&(s=i,i=void 0),"undefined"!=typeof p.easing&&(i=p.easing,delete p.easing),"undefined"!=typeof p.duration&&(n=p.duration,delete p.duration),"undefined"!=typeof p.complete&&(s=p.complete,delete p.complete),"undefined"!=typeof p.queue&&(c=p.queue,delete p.queue),"undefined"!=typeof p.delay&&(u=p.delay,delete p.delay),"undefined"==typeof n&&(n=t.fx.speeds._default),"undefined"==typeof i&&(i=t.cssEase._default),n=f(n);var l=a(p,n,i,u),h=t.transit.enabled&&d.transition,y=h?parseInt(n,10)+parseInt(u,10):0;if(0===y){var g=function(t){o.css(p),s&&s.apply(o),t&&t()};return r(o,c,g),o}var m={},v=function(e){var n=!1,i=function(){n&&o.unbind(b,i),y>0&&o.each(function(){this.style[d.transition]=m[this]||null}),"function"==typeof s&&s.apply(o),"function"==typeof e&&e()};y>0&&b&&t.transit.useTransitionEnd?(n=!0,o.bind(b,i)):window.setTimeout(i,y),o.each(function(){y>0&&(this.style[d.transition]=l),t(this).css(p)})},z=function(t){this.offsetWidth,v(t)};return r(o,c,z),this},t.transit.getTransitionValue=a,t});
define('WeltPixel_DesignElements/js/load_parallax',['jquery', 'designelements_default', 'stellar', 'jquery_important', 'jquery_transition'], function ($, SEMICOLONDEFAULT) {
    "use strict";

    (function() {
        var lastTime = 0;
        var vendors = ['ms', 'moz', 'webkit', 'o'];
        for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
            window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
                || window[vendors[x]+'CancelRequestAnimationFrame'];
        }

        if (!window.requestAnimationFrame)
            window.requestAnimationFrame = function(callback, element) {
                var currTime = new Date().getTime();
                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
                    timeToCall);
                lastTime = currTime + timeToCall;
                return id;
            };

        if (!window.cancelAnimationFrame)
            window.cancelAnimationFrame = function(id) {
                clearTimeout(id);
            };
    }());



    function debounce(func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        return function() {
            context = this;
            args = arguments;
            timestamp = new Date();
            var later = function() {
                var last = (new Date()) - timestamp;
                if (last < wait) {
                    timeout = setTimeout(later, wait - last);
                } else {
                    timeout = null;
                    if (!immediate) result = func.apply(context, args);
                }
            };
            var callNow = immediate && !timeout;
            if (!timeout) {
                timeout = setTimeout(later, wait);
            }
            if (callNow) result = func.apply(context, args);
            return result;
        };
    }


    var requesting = false;

    var killRequesting = debounce(function () {
        requesting = false;
    }, 100);


    function onScrollSliderParallax() {
        if (!requesting) {
            requesting = true;
            requestAnimationFrame(function(){
                SEMICOLONPARALLAX.widget.sliderParallax();
            });
        }
        killRequesting();
    }

    var SEMICOLONPARALLAX = SEMICOLONPARALLAX || {};
    var $body = $('body'), $window = $(window), $header = $('.page-header'), $pageTitle = $('#page-title'), $parallaxEl = $('.parallax'), $slider = $('#slider'), $sliderParallaxEl = $('.slider-parallax'), $parallaxPageTitleEl = $('.page-title-parallax'), $parallaxPortfolioEl = $('.portfolio-parallax').find('.portfolio-image');

    SEMICOLONPARALLAX.widget = {
        init: function () {
            window.addEventListener('scroll', onScrollSliderParallax, false);
            SEMICOLONPARALLAX.widget.parallax();
            $("#slider").removeClass('parallax-disabled');
            if ($slider.length) {SEMICOLONPARALLAX.widget.sliderParallax();}
        },

        parallax: function(){
            if( $parallaxEl.length > 0 || $parallaxPageTitleEl.length > 0 || $parallaxPortfolioEl.length > 0 ) {
                if( !SEMICOLONDEFAULT.isMobile.any() ){
                    $.stellar({
                        horizontalScrolling: false,
                        verticalOffset: 150
                    });
                } else {
                    $parallaxEl.addClass('mobile-parallax');
                    $parallaxPageTitleEl.addClass('mobile-parallax');
                    $parallaxPortfolioEl.addClass('mobile-parallax');
                }
            }
        },

        sliderParallaxOffset: function(){
            var sliderParallaxOffsetTop = 0;
            var headerHeight = $header.outerHeight();
            if( $body.hasClass('side-header') || $header.hasClass('transparent-header') ) { headerHeight = 0; }
            if( $pageTitle.length > 0 ) {
                var pageTitleHeight = $pageTitle.outerHeight();
                sliderParallaxOffsetTop = pageTitleHeight + headerHeight;
            } else {
                sliderParallaxOffsetTop = headerHeight;
            }

            if( $slider.next('#header').length > 0 ) { sliderParallaxOffsetTop = 0; }

            return sliderParallaxOffsetTop;
        },


        sliderParallax: function(){
            if( $sliderParallaxEl.length > 0 ) {
                if( ( $body.hasClass('device-lg') || $body.hasClass('device-md') ) && !SEMICOLONDEFAULT.isMobile.any() ) {
                    var parallaxOffsetTop = SEMICOLONPARALLAX.widget.sliderParallaxOffset(),
                        parallaxElHeight = $sliderParallaxEl.outerHeight();

                    if( ( parallaxElHeight + parallaxOffsetTop + 50 ) > $window.scrollTop() ){
                        if ($window.scrollTop() > parallaxOffsetTop) {
                            var tranformAmount = (($window.scrollTop()-parallaxOffsetTop) / 1.5 ).toFixed(2);
                            var tranformAmount2 = (($window.scrollTop()-parallaxOffsetTop) / 7 ).toFixed(2);
                            $sliderParallaxEl.stop(true,true).transition({ y: tranformAmount },0);
                            $('.slider-parallax .slider-caption,.ei-title').stop(true,true).transition({ y: -tranformAmount2 },0);
                        } else {
                            $('.slider-parallax,.slider-parallax .slider-caption,.ei-title').transition({ y: 0 },0);
                        }
                    }
                    if (requesting) {
                        requestAnimationFrame(function(){
                            SEMICOLONPARALLAX.widget.sliderParallax();
                        });
                    }
                } else {
                    $('.slider-parallax,.slider-parallax .slider-caption,.ei-title').transition({ y: 0 },0);
                }
            }
        },
    };

    return SEMICOLONPARALLAX;
});

(function(root) {
define("fancybox", [], function() {
  return (function() {
/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */
(function(r,G,f,v){var J=f("html"),n=f(r),p=f(G),b=f.fancybox=function(){b.open.apply(this,arguments)},I=navigator.userAgent.match(/msie/i),B=null,s=G.createTouch!==v,t=function(a){return a&&a.hasOwnProperty&&a instanceof f},q=function(a){return a&&"string"===f.type(a)},E=function(a){return q(a)&&0<a.indexOf("%")},l=function(a,d){var e=parseInt(a,10)||0;d&&E(a)&&(e*=b.getViewport()[d]/100);return Math.ceil(e)},w=function(a,b){return l(a,b)+"px"};f.extend(b,{version:"2.1.5",defaults:{padding:15,margin:20,
width:800,height:600,minWidth:100,minHeight:100,maxWidth:9999,maxHeight:9999,pixelRatio:1,autoSize:!0,autoHeight:!1,autoWidth:!1,autoResize:!0,autoCenter:!s,fitToView:!0,aspectRatio:!1,topRatio:0.5,leftRatio:0.5,scrolling:"auto",wrapCSS:"",arrows:!0,closeBtn:!0,closeClick:!1,nextClick:!1,mouseWheel:!0,autoPlay:!1,playSpeed:3E3,preload:3,modal:!1,loop:!0,ajax:{dataType:"html",headers:{"X-fancyBox":!0}},iframe:{scrolling:"auto",preload:!0},swf:{wmode:"transparent",allowfullscreen:"true",allowscriptaccess:"always"},
keys:{next:{13:"left",34:"up",39:"left",40:"up"},prev:{8:"right",33:"down",37:"right",38:"down"},close:[27],play:[32],toggle:[70]},direction:{next:"left",prev:"right"},scrollOutside:!0,index:0,type:null,href:null,content:null,title:null,tpl:{wrap:'<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',image:'<img class="fancybox-image" src="{href}" alt="" />',iframe:'<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen'+
(I?' allowtransparency="true"':"")+"></iframe>",error:'<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',closeBtn:'<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',next:'<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',prev:'<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0,
openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1,
isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=t(a)?f(a).get():[a]),f.each(a,function(e,c){var k={},g,h,j,m,l;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),t(c)?(k={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,k,
c.metadata())):k=c);g=d.href||k.href||(q(c)?c:null);h=d.title!==v?d.title:k.title||"";m=(j=d.content||k.content)?"html":d.type||k.type;!m&&k.isDom&&(m=c.data("fancybox-type"),m||(m=(m=c.prop("class").match(/fancybox\.(\w+)/))?m[1]:null));q(g)&&(m||(b.isImage(g)?m="image":b.isSWF(g)?m="swf":"#"===g.charAt(0)?m="inline":q(c)&&(m="html",j=c)),"ajax"===m&&(l=g.split(/\s+/,2),g=l.shift(),l=l.shift()));j||("inline"===m?g?j=f(q(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):k.isDom&&(j=c):"html"===m?j=g:!m&&(!g&&
k.isDom)&&(m="inline",j=c));f.extend(k,{href:g,type:m,content:j,title:h,selector:l});a[e]=k}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==v&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current||
b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer=
setTimeout(b.next,b.current.playSpeed))},c=function(){d();p.unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index<b.group.length-1))b.player.isActive=!0,p.bind({"onCancel.player beforeClose.player":c,"onUpdate.player":e,"beforeLoad.player":d}),e(),b.trigger("onPlayStart")}else c()},next:function(a){var d=b.current;d&&(q(a)||(a=d.direction.next),b.jumpto(d.index+1,a,"next"))},prev:function(a){var d=b.current;
d&&(q(a)||(a=d.direction.prev),b.jumpto(d.index-1,a,"prev"))},jumpto:function(a,d,e){var c=b.current;c&&(a=l(a),b.direction=d||c.direction[a>=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==v&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,k;c&&(k=b._getPosition(d),a&&"scroll"===a.type?(delete k.position,c.stop(!0,!0).animate(k,200)):(c.css(k),e.pos=f.extend({},e.dim,k)))},update:function(a){var d=
a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(B),B=null);b.isOpen&&!B&&(B=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),B=null)},e&&!s?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,s&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"),b.trigger("onUpdate")),
b.update())},hideLoading:function(){p.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('<div id="fancybox-loading"><div></div></div>').click(b.cancel).appendTo("body");p.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked||!1,d={x:n.scrollLeft(),
y:n.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=s&&r.innerWidth?r.innerWidth:n.width(),d.h=s&&r.innerHeight?r.innerHeight:n.height());return d},unbindEvents:function(){b.wrap&&t(b.wrap)&&b.wrap.unbind(".fb");p.unbind(".fb");n.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(n.bind("orientationchange.fb"+(s?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&p.bind("keydown.fb",function(e){var c=e.which||e.keyCode,k=e.target||e.srcElement;
if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!k||!k.type&&!f(k).is("[contenteditable]")))&&f.each(d,function(d,k){if(1<a.group.length&&k[c]!==v)return b[d](k[c]),e.preventDefault(),!1;if(-1<f.inArray(c,k))return b[d](),e.preventDefault(),!1})}),f.fn.mousewheel&&a.mouseWheel&&b.wrap.bind("mousewheel.fb",function(d,c,k,g){for(var h=f(d.target||null),j=!1;h.length&&!j&&!h.is(".fancybox-skin")&&!h.is(".fancybox-wrap");)j=h[0]&&!(h[0].style.overflow&&"hidden"===h[0].style.overflow)&&
(h[0].clientWidth&&h[0].scrollWidth>h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!j&&1<b.group.length&&!a.canShrink){if(0<g||0<k)b.prev(0<g?"down":"left");else if(0>g||0>k)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,e){if(e&&b.helpers[d]&&f.isFunction(b.helpers[d][a]))b.helpers[d][a](f.extend(!0,
{},b.helpers[d].defaults,e),c)});p.trigger(a)}},isImage:function(a){return q(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)},isSWF:function(a){return q(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=l(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&(d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,
mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=!0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=
!0);"iframe"===c&&s&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(s?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,w(d.padding[a]))});b.trigger("onReady");if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");
"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width=this.width/b.opts.pixelRatio;b.coming.height=this.height/b.opts.pixelRatio;b._afterLoad()};a.onerror=function(){this.onload=
this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g,(new Date).getTime())).attr("scrolling",s?"auto":a.iframe.scrolling).attr("src",a.href);
f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);s||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a=b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,
e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,k,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents();e=a.content;c=a.type;k=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,
outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("<div>").html(e).find(a.selector):t(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('<div class="fancybox-placeholder"></div>').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder",!1)}));break;case "image":e=a.tpl.image.replace("{href}",
g);break;case "swf":e='<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="'+g+'"></param>',h="",f.each(a.swf,function(a,b){e+='<param name="'+a+'" value="'+b+'"></param>';h+=" "+a+'="'+b+'"'}),e+='<embed src="'+g+'" type="application/x-shockwave-flash" width="100%" height="100%"'+h+"></embed></object>"}(!t(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow");a.inner.css("overflow","yes"===k?"scroll":
"no"===k?"hidden":k);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,k=b.skin,g=b.inner,h=b.current,c=h.width,j=h.height,m=h.minWidth,u=h.minHeight,n=h.maxWidth,p=h.maxHeight,s=h.scrolling,q=h.scrollOutside?
h.scrollbarWidth:0,x=h.margin,y=l(x[1]+x[3]),r=l(x[0]+x[2]),v,z,t,C,A,F,B,D,H;e.add(k).add(g).width("auto").height("auto").removeClass("fancybox-tmp");x=l(k.outerWidth(!0)-k.width());v=l(k.outerHeight(!0)-k.height());z=y+x;t=r+v;C=E(c)?(a.w-z)*l(c)/100:c;A=E(j)?(a.h-t)*l(j)/100:j;if("iframe"===h.type){if(H=h.content,h.autoHeight&&1===H.data("ready"))try{H[0].contentWindow.document.location&&(g.width(C).height(9999),F=H.contents().find("body"),q&&F.css("overflow-x","hidden"),A=F.outerHeight(!0))}catch(G){}}else if(h.autoWidth||
h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(C),h.autoHeight||g.height(A),h.autoWidth&&(C=g.width()),h.autoHeight&&(A=g.height()),g.removeClass("fancybox-tmp");c=l(C);j=l(A);D=C/A;m=l(E(m)?l(m,"w")-z:m);n=l(E(n)?l(n,"w")-z:n);u=l(E(u)?l(u,"h")-t:u);p=l(E(p)?l(p,"h")-t:p);F=n;B=p;h.fitToView&&(n=Math.min(a.w-z,n),p=Math.min(a.h-t,p));z=a.w-y;r=a.h-r;h.aspectRatio?(c>n&&(c=n,j=l(c/D)),j>p&&(j=p,c=l(j*D)),c<m&&(c=m,j=l(c/D)),j<u&&(j=u,c=l(j*D))):(c=Math.max(m,Math.min(c,n)),h.autoHeight&&
"iframe"!==h.type&&(g.width(c),j=g.height()),j=Math.max(u,Math.min(j,p)));if(h.fitToView)if(g.width(c).height(j),e.width(c+x),a=e.width(),y=e.height(),h.aspectRatio)for(;(a>z||y>r)&&(c>m&&j>u)&&!(19<d++);)j=Math.max(u,Math.min(p,j-10)),c=l(j*D),c<m&&(c=m,j=l(c/D)),c>n&&(c=n,j=l(c/D)),g.width(c).height(j),e.width(c+x),a=e.width(),y=e.height();else c=Math.max(m,Math.min(c,c-(a-z))),j=Math.max(u,Math.min(j,j-(y-r)));q&&("auto"===s&&j<A&&c+x+q<z)&&(c+=q);g.width(c).height(j);e.width(c+x);a=e.width();
y=e.height();e=(a>z||y>r)&&c>m&&j>u;c=h.aspectRatio?c<F&&j<B&&c<C&&j<A:(c<F||j<B)&&(c<C||j<A);f.extend(h,{dim:{width:w(a),height:w(y)},origWidth:C,origHeight:A,canShrink:e,canExpand:c,wPadding:x,hPadding:v,wrapSpace:y-k.outerHeight(!0),skinSpace:k.height()-j});!H&&(h.autoHeight&&j>u&&j<p&&!c)&&g.height("auto")},_getPosition:function(a){var d=b.current,e=b.getViewport(),c=d.margin,f=b.wrap.width()+c[1]+c[3],g=b.wrap.height()+c[0]+c[2],c={position:"absolute",top:c[0],left:c[3]};d.autoCenter&&d.fixed&&
!a&&g<=e.h&&f<=e.w?c.position="fixed":d.locked||(c.top+=e.y,c.left+=e.x);c.top=w(Math.max(c.top,c.top+(e.h-g)*d.topRatio));c.left=w(Math.max(c.left,c.left+(e.w-f)*d.leftRatio));return c},_afterZoomIn:function(){var a=b.current;a&&(b.isOpen=b.isOpened=!0,b.wrap.css("overflow","visible").addClass("fancybox-opened"),b.update(),(a.closeClick||a.nextClick&&1<b.group.length)&&b.inner.css("cursor","pointer").bind("click.fb",function(d){!f(d.target).is("a")&&!f(d.target).parent().is("a")&&(d.preventDefault(),
b[a.closeClick?"close":"next"]())}),a.closeBtn&&f(a.tpl.closeBtn).appendTo(b.skin).bind("click.fb",function(a){a.preventDefault();b.close()}),a.arrows&&1<b.group.length&&((a.loop||0<a.index)&&f(a.tpl.prev).appendTo(b.outer).bind("click.fb",b.prev),(a.loop||a.index<b.group.length-1)&&f(a.tpl.next).appendTo(b.outer).bind("click.fb",b.next)),b.trigger("afterShow"),!a.loop&&a.index===a.group.length-1?b.play(!1):b.opts.autoPlay&&!b.player.isActive&&(b.opts.autoPlay=!1,b.play()))},_afterZoomOut:function(a){a=
a||b.current;f(".fancybox-wrap").trigger("onReset").remove();f.extend(b,{group:{},opts:{},router:!1,current:null,isActive:!1,isOpened:!1,isOpen:!1,isClosing:!1,wrap:null,skin:null,outer:null,inner:null});b.trigger("afterClose",a)}});b.transitions={getOrigPosition:function(){var a=b.current,d=a.element,e=a.orig,c={},f=50,g=50,h=a.hPadding,j=a.wPadding,m=b.getViewport();!e&&(a.isDom&&d.is(":visible"))&&(e=d.find("img:first"),e.length||(e=d));t(e)?(c=e.offset(),e.is("img")&&(f=e.outerWidth(),g=e.outerHeight())):
(c.top=m.y+(m.h-g)*a.topRatio,c.left=m.x+(m.w-f)*a.leftRatio);if("fixed"===b.wrap.css("position")||a.locked)c.top-=m.y,c.left-=m.x;return c={top:w(c.top-h*a.topRatio),left:w(c.left-j*a.leftRatio),width:w(f+j),height:w(g+h)}},step:function(a,d){var e,c,f=d.prop;c=b.current;var g=c.wrapSpace,h=c.skinSpace;if("width"===f||"height"===f)e=d.end===d.start?1:(a-d.start)/(d.end-d.start),b.isClosing&&(e=1-e),c="width"===f?c.wPadding:c.hPadding,c=a-c,b.skin[f](l("width"===f?c:c-g*e)),b.inner[f](l("width"===
f?c:c-g*e-h*e))},zoomIn:function(){var a=b.current,d=a.pos,e=a.openEffect,c="elastic"===e,k=f.extend({opacity:1},d);delete k.position;c?(d=this.getOrigPosition(),a.openOpacity&&(d.opacity=0.1)):"fade"===e&&(d.opacity=0.1);b.wrap.css(d).animate(k,{duration:"none"===e?0:a.openSpeed,easing:a.openEasing,step:c?this.step:null,complete:b._afterZoomIn})},zoomOut:function(){var a=b.current,d=a.closeEffect,e="elastic"===d,c={opacity:0.1};e&&(c=this.getOrigPosition(),a.closeOpacity&&(c.opacity=0.1));b.wrap.animate(c,
{duration:"none"===d?0:a.closeSpeed,easing:a.closeEasing,step:e?this.step:null,complete:b._afterZoomOut})},changeIn:function(){var a=b.current,d=a.nextEffect,e=a.pos,c={opacity:1},f=b.direction,g;e.opacity=0.1;"elastic"===d&&(g="down"===f||"up"===f?"top":"left","down"===f||"right"===f?(e[g]=w(l(e[g])-200),c[g]="+=200px"):(e[g]=w(l(e[g])+200),c[g]="-=200px"));"none"===d?b._afterZoomIn():b.wrap.css(e).animate(c,{duration:a.nextSpeed,easing:a.nextEasing,complete:b._afterZoomIn})},changeOut:function(){var a=
b.previous,d=a.prevEffect,e={opacity:0.1},c=b.direction;"elastic"===d&&(e["down"===c||"up"===c?"top":"left"]=("up"===c||"left"===c?"-":"+")+"=200px");a.wrap.animate(e,{duration:"none"===d?0:a.prevSpeed,easing:a.prevEasing,complete:function(){f(this).trigger("onReset").remove()}})}};b.helpers.overlay={defaults:{closeClick:!0,speedOut:200,showEarly:!0,css:{},locked:!s,fixed:!0},overlay:null,fixed:!1,el:f("html"),create:function(a){a=f.extend({},this.defaults,a);this.overlay&&this.close();this.overlay=
f('<div class="fancybox-overlay"></div>').appendTo(b.coming?b.coming.parent:a.parent);this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(n.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){if(f(a.target).hasClass("fancybox-overlay"))return b.isActive?
b.close():d.close(),!1});this.overlay.css(a.css).show()},close:function(){var a,b;n.unbind("resize.overlay");this.el.hasClass("fancybox-lock")&&(f(".fancybox-margin").removeClass("fancybox-margin"),a=n.scrollTop(),b=n.scrollLeft(),this.el.removeClass("fancybox-lock"),n.scrollTop(a).scrollLeft(b));f(".fancybox-overlay").remove().hide();f.extend(this,{overlay:null,fixed:!1})},update:function(){var a="100%",b;this.overlay.width(a).height("100%");I?(b=Math.max(G.documentElement.offsetWidth,G.body.offsetWidth),
p.width()>b&&(a=p.width())):p.width()>n.width()&&(a=p.width());this.overlay.width(a).height(p.height())},onReady:function(a,b){var e=this.overlay;f(".fancybox-overlay").stop(!0,!0);e||this.create(a);a.locked&&(this.fixed&&b.fixed)&&(e||(this.margin=p.height()>n.height()?f("html").css("margin-right").replace("px",""):!1),b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){var e,c;b.locked&&(!1!==this.margin&&(f("*").filter(function(){return"fixed"===
f(this).css("position")&&!f(this).hasClass("fancybox-overlay")&&!f(this).hasClass("fancybox-wrap")}).addClass("fancybox-margin"),this.el.addClass("fancybox-margin")),e=n.scrollTop(),c=n.scrollLeft(),this.el.addClass("fancybox-lock"),n.scrollTop(e).scrollLeft(c));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.coming&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d=
b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(q(e)&&""!==f.trim(e)){d=f('<div class="fancybox-title fancybox-title-'+c+'-wrap">'+e+"</div>");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),I&&d.width(d.width()),d.wrapInner('<span class="child"></span>'),b.current.margin[2]+=Math.abs(l(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d,
e=f(this),c=this.selector||"",k=function(g){var h=f(this).blur(),j=d,k,l;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(k=a.groupAttr||"data-fancybox-group",l=h.attr(k),l||(k="rel",l=h.get(0)[k]),l&&(""!==l&&"nofollow"!==l)&&(h=c.length?f(c):e,h=h.filter("["+k+'="'+l+'"]'),j=h.index(this)),a.index=j,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",k):p.undelegate(c,"click.fb-start").delegate(c+
":not('.fancybox-item, .fancybox-nav')","click.fb-start",k);this.filter("[data-fancybox-start=1]").trigger("click");return this};p.ready(function(){var a,d;f.scrollbarWidth===v&&(f.scrollbarWidth=function(){var a=f('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo("body"),b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===v){a=f.support;d=f('<div style="position:fixed;top:20px;"></div>').appendTo("body");var e=20===
d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")});a=f(r).width();J.addClass("fancybox-lock-test");d=f(r).width();J.removeClass("fancybox-lock-test");f("<style type='text/css'>.fancybox-margin{margin-right:"+(d-a)+"px;}</style>").appendTo("head")})})(window,document,jQuery);
return jQuery.fn.fancybox;
  }).apply(root, arguments);
});
}(this));

/*
    Author  : I.CUBE, inc.
    main jQuery widget of Fpmini
*/

define('planetsports',[
    'jquery',
    'jquery/ui',
    'mage/translate',
    'Born_WeltPixelGtm/js/born_gtm',
    'Magento_Customer/js/customer-data',
    'owl_carousel',
    'fancybox'
], function ($, ui, _responsive, born_gtm, customerData) {
    'use strict';

    $.widget('planetsports.planetsports', {

        _create: function () {
            let self = this;

            // Wait for the DOM to be fully loaded before running the functions.
            
            $(document).ready(function() {
                self.initAllPages();
                self.initCheckoutPage();
            });
        },

        initAllPages: function () {

            /* Fixed - Mobile menu arrow and back button  */
            if ($(window).width() < 769) {
                var htmlDrillOpener = '<span class="drill-opener"></span>';
                var htmlDrillDown = '<div class="drilldown-back"><a href="#"><span class="drill-opener"></span><span class="current-cat"></span></a></div>';
                if ($(".ves-megamenu .before-ves-submenu-inner").length) {
                    $(".ves-megamenu .before-ves-submenu-inner").parent().prepend($(htmlDrillDown));
                }
                if ($(".ves-megamenu .nav-anchor > .opener").length) {
                    $(".ves-megamenu .nav-anchor > .opener").parent().append($(htmlDrillOpener));
                }
            }

            /*Fix for toogle opener not showing for menu*/
            $('.nav-toggle').on('click', function() {
                $('.ves-megamenu .opener').each(function () {
                    if (!$(this).siblings('.drill-opener').length) {
                        var drillOpener = $('<span class="drill-opener"></span>');
                        $(this).after(drillOpener);
                    }
                });
            });

            /* FIXED - On checkout observed same address two times*/
            if ($('body').hasClass("checkout-onepage-success")) {
                localStorage.setItem('eds-active', false);
                customerData.invalidate(['checkout-data']);
                customerData.reload(['checkout-data'], true);
                window.localStorage.removeItem('cardBankName');
                window.localStorage.removeItem('creditCardInstallment');
                window.localStorage.removeItem('installmentText');
                window.localStorage.removeItem('creditCardHandlingFee');
            }

            $("#copyToClipboard").click(function(){
                $("#copyToClipboard").html("Copied");
            });

            // ----move header links to nav section----
            $('.page-wrapper > .header.links').appendTo(".page-wrapper .sections.nav-sections .section-items");
            $('.page-wrapper .sections.nav-sections .section-items .header.links').css("display", "block");
            // ----move header links to nav section----

            // ----move copyright below newsletter----
            if ($(window).width() > 639) {
                $('.page-wrapper .copyright').appendTo(".footer-container .right > .newsletter > div > .copyright");
            }
            // ----move copyright below newsletter----
            // ---- mobile menu click
            $('.top-menu.nav-mobile *').click(function () {
                var interval = setInterval(function () {
                    if ($('.top-menu.nav-mobile .navigation').hasClass("loaded")) {
                        if ($('.top-menu.nav-mobile .navigation > .dropdown-menu').length) {
                            $('.top-menu.nav-mobile').css({ 'min-height': 'auto' });
                            $('.sections.nav-sections .additional-mobile').show();
                            $('.sections.nav-sections .additional-login-mobile').hide();
                            $('.sections.nav-sections #multistore-mobile-switcher-language').show();
                            $('.sections.nav-sections .store-information').show();
                            clearInterval(interval);
                        } else {
                            $('.sections.nav-sections .additional-mobile').show();
                            $('.sections.nav-sections #multistore-mobile-switcher-language').show();
                            $('.sections.nav-sections .store-information').show();
                            $('.top-menu.nav-mobile').css({ 'min-height': 'auto' });
                            if ($(".page-header .header.links > .customer-welcome").length) {
                                $('.sections.nav-sections .additional-login-mobile').show();
                            }
                            clearInterval(interval);
                        }
                    }
                }, 50);
            });

            //Sticky Add to cart_
            if ($(window).width() < 769) {
                var pdpsticky = $('.catalog-product-view .box-tocart');
                pdpsticky.addClass('pdpsticky');
            }

            $(".mm__sections .mm__links .mm__links-title").click(function () {
                if ($(window).width() < 767) {
                    $(this).parent().toggleClass("open");
                }
            });

            $(document).on('click', '.drill-opener', function () {
                if($(window).width() < 767) {
                    if ($(this).closest('.nav-item').length) {
                        let levelClass = Array.from($(this).closest('.nav-item')[0].classList)
                            .find(c => /^level\d+$/.test(c));

                        if (levelClass) {
                            $('.nav-sections').attr('data-previous-level', levelClass);
                        }

                    } else {
                        let prevLevelClass = $('.nav-sections').attr('data-previous-level');
                        if (prevLevelClass) {
                            let prevLevelNum = parseInt(prevLevelClass.replace('level', ''), 10);

                            if (!isNaN(prevLevelNum)) {
                                if (prevLevelNum > 0) {
                                    $('.nav-sections').attr('data-previous-level', 'level' + (prevLevelNum - 1));
                                } else {
                                    $('.nav-sections').removeAttr('data-previous-level');
                                }
                            }
                        }
                    }
                }
            });

            // ---- mobile menu click
            // ---- STICKY HEADER ----

            $.noConflict();
               $(document).ready(function ($) {
                    if ($('body').hasClass("checkout-index-index") && $('body').hasClass("store-view-en_sg")) {
                        var checkoutIntervalRegion = setInterval(function () {
                            if ($('#shipping-new-address-form').find('select[name="region_id"] option').length > 1) {
                                $('#shipping-new-address-form').find('select[name="region_id"] option:last').attr('selected','selected');
                                $('#shipping-new-address-form').find('select[name="region_id"]').trigger('change');
                                $('#shipping-new-address-form').find('select[name="region_id"] option:last').prop('selected', true);
                                $('#shipping-new-address-form').find('select[name="region_id"]').attr('readonly',true);
                                clearInterval(checkoutIntervalRegion);
                            }
                        }, 100);
                        var checkoutIntervalCity = setInterval(function () {
                            if ($('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"] option').length > 1) {
                                $('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"] option:last').attr('selected','selected');
                                $('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"]').trigger('change');
                                $('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"] option:last').prop('selected', true);
                                $('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"]').attr('readonly',true);
                                var lastOptVal = $('#shipping-new-address-form').find('select[name="custom_attributes[city_id]"] option:last').val();
                                $('#shipping-new-address-form').find('input[name="city"]').val(lastOptVal);
                                clearInterval(checkoutIntervalCity);
                            }
                        }, 100);
                        var checkoutIntervalKecamatan = setInterval(function () {
                            if ($('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option').length > 1) {
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').attr('selected','selected');
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').trigger('change');
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').prop('selected', true);
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').attr('readonly',true);
                                clearInterval(checkoutIntervalKecamatan);
                            }
                        }, 100);
                    }

                    if ($('body').hasClass("customer-address-form") && $('body').hasClass("store-view-en_sg")) {
                        var initIntervalRegion = setInterval(function () {
                            if ($('.form-address-edit').find('select[name="region_id"] option').length > 1) {
                                $('.form-address-edit').find('select[name="region_id"] option:last').attr('selected','selected');
                                $('.form-address-edit').find('select[name="region_id"]').trigger('change');
                                $('.form-address-edit').find('select[name="region_id"] option:last').prop('selected', true);
                                $('.form-address-edit').find('select[name="region_id"]').attr('readonly',true);
                                clearInterval(initIntervalRegion);
                            }
                        }, 100);
                        var initIntervalCity = setInterval(function () {
                            if ($('.form-address-edit').find('select[name="cityCopy"] option').length > 1) {
                                $('.form-address-edit').find('select[name="cityCopy"] option:last').attr('selected','selected');
                                $('.form-address-edit').find('select[name="cityCopy"]').trigger('change');
                                $('.form-address-edit').find('select[name="cityCopy"] option:last').prop('selected', true);
                                $('.form-address-edit').find('select[name="cityCopy"]').attr('readonly',true);
                                var lastOptVal = $('.form-address-edit').find('select[name="cityCopy"] option:last').val();
                                $('.form-address-edit').find('input[name="city"]').val(lastOptVal);
                                clearInterval(initIntervalCity);
                            }
                        }, 100);
                        var initIntervalKecamatan = setInterval(function () {
                            if ($('.form-address-edit').find('select[name="kecamatan"] option').length > 1) {
                                $('.form-address-edit').find('select[name="kecamatan"] option:last').attr('selected','selected');
                                $('.form-address-edit').find('select[name="kecamatan"]').trigger('change');
                                $('.form-address-edit').find('select[name="kecamatan"] option:last').prop('selected', true);
                                $('.form-address-edit').find('select[name="kecamatan"]').attr('readonly',true);
                                clearInterval(initIntervalKecamatan);
                            }
                        }, 100);
                    }

                   if ($('body').hasClass("checkout-index-index") &&
                       ($('body').hasClass("store-view-en_th") || $('body').hasClass("store-view-th_th"))
                   ) {
                       var InitInterval = setInterval(function () {
                           if ($('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option').length > 1) {
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').attr('selected', 'selected');
                                var lastOptVal = $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').val();
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').val(lastOptVal);
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').trigger('change');
                                $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').prop('selected', true);
                                clearInterval(InitInterval);
                           }
                       },100);
                       $(document).on('change blur', '[name="custom_attributes[city_id]"]', function () {
                           var InitIntervalChange = setInterval(function () {
                               if ($('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option').length > 1) {
                                    $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').attr('selected', 'selected');
                                    var lastOptVal = $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').val();
                                    $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').val(lastOptVal);
                                    $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"]').trigger('change');
                                    $('#shipping-new-address-form').find('select[name="custom_attributes[district_id]"] option:last').prop('selected', true);
                                    clearInterval(InitIntervalChange);
                               }
                           },100);
                       });
                   }

                   if ($('body').hasClass("customer-address-form") &&
                       ($('body').hasClass("store-view-en_th") || $('body').hasClass("store-view-th_th"))
                   ) {
                       var InitIntervalCity = setInterval(function () {
                           if ($('.form-address-edit').find('select[name="cityCopy"] option').length > 1) {
                               var cityVal = $('.form-address-edit').find('input[name="city"]').val();
                               $('.form-address-edit').find('select[name="cityCopy"]').val(cityVal);
                               $('.form-address-edit').find('select[name="cityCopy"]').trigger('change');
                               clearInterval(InitIntervalCity);
                           }
                       },100);
                       var InitInterval = setInterval(function () {
                           if ($('.form-address-edit').find('select[name="kecamatan"] option').length > 1) {
                               $('.form-address-edit').find('select[name="kecamatan"] option:last').attr('selected','selected');
                               $('.form-address-edit').find('select[name="kecamatan"]').trigger('change');
                               $('.form-address-edit').find('select[name="kecamatan"] option:last').prop('selected', true);
                               clearInterval(InitInterval);
                           }
                       },100);
                       $(document).on('change blur', 'select[name="cityCopy"]', function () {
                           $('.form-address-edit').find('input[name="city"]').val($(this).val());
                           var InitIntervalChange = setInterval(function () {
                               if ($('.form-address-edit').find('select[name="kecamatan"] option').length > 1) {
                                   $('.form-address-edit').find('select[name="kecamatan"] option:last').attr('selected','selected');
                                   $('.form-address-edit').find('select[name="kecamatan"]').trigger('change');
                                   $('.form-address-edit').find('select[name="kecamatan"] option:last').prop('selected', true);
                                   clearInterval(InitIntervalChange);
                               }
                           },100);
                       });
                   }

                });

            //$('<div class="scroll-div" />').prependTo('.page-wrapper');
            if ($(window).width() > 767) {
              if (!$('body').hasClass("checkout-index-index")) {
                  var stickyTop = ($('.header-main').offset() || { "top": NaN }).top;
                  $(window).on('scroll', function () {
                      if ($(window).scrollTop() >= stickyTop) {
                          $('body').addClass('fixed-header');
                          $('.header-main').css({ position: "fixed", top: "0px", width: "100%" });
                          $('#search_bar_container').css({ top: "6rem"});
                          $('.weltpixel_multistore').removeClass('open');
                          $('.h-navigation').css({ position: "fixed", top: "78px", width: "100%" });
                          $('.header-top-wrap').hide();

                      } else {
                          $('body').removeClass('fixed-header');
                          $('.header-main').css({ position: "relative", top: "0px" });
                          $('.h-navigation').css({ position: "unset" });
                          $('#search_bar_container').css({ top: "10rem"});
                          $('.header-top-wrap').show(); 
                      }
                  });
              }
            }
            function stickHeight() {
                if ($(window).width() > 767) {
                    var h = $('header.page-header').height();
                    $('.scroll-div').height(h);
                } else {
                    var h = $('.header-main').height();
                }
            }
            stickHeight();
            $(window).resize(function () {
                stickHeight();
            });
            // if ($(window).width() > 767) {
            //     $(window).scroll(function () {
            //         $(window).scrollTop($(window).scrollTop());
            //         if ($(window).scrollTop() === 0) {
            //             $('.scroll-div').css('margin-top', 0);
            //             if ($(window).width() > 767) {
            //                 $('.header-top-wrap').slideDown('500');
            //             }
            //         } else {
            //            $('.scroll-div').css('margin-top', -100);
            //             if ($(window).width() > 767) {
            //                 $('.header-top-wrap').slideUp('500');
            //             }
            //         }
            //     });
            // }
            $(document).ready(function () {
                var featured = $('.recomendation-slider-homepage .widget-product-grid');
                featured.owlCarousel({
                    items: 5,
                    stagePadding: 50,
                    slideSpeed: 800,
                    nav: false,
                    dots: false,
                    autoplay: false,
                    loop: false,
                    smartSpeed: 1500,
                    responsive: {
                        // breakpoint from 0 up
                        0: {
                            items: 1,
                            nav: false,
                            dots: false
                        },
                        // breakpoint from 480 up
                        480: {
                            items: 2,
                            nav: false,
                            dots: false
                        },
                        // breakpoint from 768 up
                        768: {
                            items: 4,
                            nav: true,
                            dots: false
                        },
                        // breakpoint from 768 up
                        940: {
                            items: 5,
                            nav: true,
                            dots: false
                        }
                    }
                });
            });
            $(document).ready(function () {
                $('.checkout-cart-index .discount-trigger').click(function(e){
                    e.preventDefault();
                    $('.checkout-cart-index #block-discount').toggleClass('active');
                    $('.checkout-cart-index #block-discount .content').slideToggle("active");
                });
                $('.checkout-cart-index .eds-discount-trigger').click(function(e){
                    e.preventDefault();
                    $('.checkout-cart-index #block-eds-discount').toggleClass('active');
                    $('.checkout-cart-index #block-eds-discount .content').slideToggle("active");
                });
                setTimeout(function() {
                    $('.slider-loader').css({"opacity": "0"});
                }, 500);
                setTimeout(function() {
                    $('.slider-loader').css({"display": "none"});
                }, 1500);
                window.onload = function() {
                    // $('.catalog-category-view .product_image .product-image-photo').css({"opacity": "1"});
                    // $('.catalogsearch-result-index .product_image .product-image-photo').css({"opacity": "1"});
                    // $('.catalogsearch-advanced-result .product_image .product-image-photo').css({"opacity": "1"});
                };
                $('.checkout-container .price.discount-price').closest('.price-excluding-tax').addClass('has-discount');
                if ($(window).width() >767){
                    $(document).on({
                        mouseenter: function () {
                            $(this).children('.product_image').addClass("hover-image");
                            $(this).children('.product.details').addClass("hover-color");
                            if($(this).children().find('.swatch-attribute-options').length>0){
                                $(this).addClass('swatch-options-scale');
                            }
                        },
                        mouseleave: function () {
                            $(this).children('.product_image').removeClass("hover-image");
                            $(this).children('.product.details').removeClass("hover-color");
                        }
                    }, '.product-item-info');
                }
                var windowWidthPdp = $(window).width();
                if (windowWidthPdp < 768) {
                    $('.search-result ul').on('click', 'li', function() {
                        setTimeout(function () {
                            $('.catalog-product-view .modal-popup').animate({ scrollTop: $('#stores-map').offset().top - 100 }, 'slow');
                        }, 50)
                    });
                }
                var max = 0;
                $('.magnifier-preview').css({"width": "550px !important", "height":"550px !important"});
                $('.multistore-switcher ul.weltpixel_multistore li.active.switcher-option').clone().prependTo(".language-dropdown-container");
                $(".footer-container .collapsible .title").click(function () {
                    $(this).parent().toggleClass("open");
                });

                setTimeout(function(){
                    if (typeof JSON.parse(localStorage.getItem('mage-cache-storage')).customer !== 'undefined'
                        && JSON.parse(localStorage.getItem('mage-cache-storage')).customer.fullname !== null) {
                        var customerFullName = JSON.parse(localStorage.getItem('mage-cache-storage')).customer.fullname;
                        $('.customer-name .customer-fullname').html(customerFullName);
                    }
                }, 1500);


                $(window).resize(function() {
                    if ($(window).width() < 801) {
                        if ($(".page-header .header.links > .customer-welcome").length) {
                            $(".additional-login-mobile").show();
                            //$(".login-mobile").hide();
                        } else if ($(".page-header .header.links > .authorization-link").length) {
                            $(".additional-login-mobile").hide();
                            //$(".login-mobile").show();
                        }
                        $('.top-menu.nav-mobile').find('.active').removeClass('active');
                    } else {
                        $(".additional-login-mobile").hide();
                        //$(".login-mobile").show();
                    }
                });

                var interval = setInterval(function () {
                    if ($(window).width() < 801) {
                        $('.top-menu.nav-mobile').find('.active').removeClass('active');
                        if ($(".page-header .header.links > .customer-welcome").length) {
                            $(".additional-login-mobile").show();
                            //$(".login-mobile").hide();
                            clearInterval(interval);
                        } else if ($(".page-header .header.links > .authorization-link").length) {
                            $(".additional-login-mobile").hide();
                            //$(".login-mobile").show();
                            clearInterval(interval);
                        }
                    } else {
                        $(".additional-login-mobile").hide();
                        //$(".login-mobile").show();
                        clearInterval(interval);
                    }
                    if (typeof JSON.parse(localStorage.getItem('mage-cache-storage')).customer !== 'undefined'
                        && JSON.parse(localStorage.getItem('mage-cache-storage')).customer.fullname !== null) {
                        var customerFullName = JSON.parse(localStorage.getItem('mage-cache-storage')).customer.fullname;
                        $('.customer-name .customer-fullname').html(customerFullName);
                    }
                }, 1000);

                $(document).on('click', function(e) {
                    var container = $("#suggestionsContainer");
                    if (!$(e.target).closest(container).length) {
                        container.addClass("ng-hide");
                    }
                });

                if($('body').hasClass('cms-surprise-drop-event')){$('body').addClass('catalog-category-view');}
            });


            //searchsuggestion mobile click events

            $('[data-role="minisearch-label"]').on('click', function () {
                $(this).closest('.minisearch').addClass('search-active');
                $(this).addClass('search-active');
            });

           
            $(document).on('click', function(event) {
                if (!$(event.target).closest('.block-search, #search_bar_container, [data-role="minisearch-label"]').length) {
                    $('[data-role="minisearch-label"]').removeClass('search-active');
                    $('.minisearch').removeClass('search-active');
                    $('#search_bar_container .unbxd-as-wrapper').hide();
                }
            });
            
            $('.cel-ac-icon-cancel').on('click', function () {
                $('[data-role="minisearch-label"]').removeClass('search-active');
                $('.minisearch').removeClass('search-active');
            });

            //Faq Page Email url changes starts Here
            $(document).ready(function () {
                //console.log("Faq pages");
                setTimeout(function () {
                    var url = window.location.href;
                    var page = url.substring(url.lastIndexOf('/') + 1);
                    if (page == 'faq#payment') {
                        $('#payment .togglet').addClass('toggleta');
                        $('#payment .togglec').css('display', 'block');
                        console.log("FAQ" + page);
                        if ($(window).width() < 768) {
                            $('html, body').animate({
                                scrollTop: $("#payment").offset().top - 100
                            }, 2000);
                        }
                        if ($(window).width() > 767) {
                            $('html, body').animate({
                                scrollTop: $("#payment").offset().top - 250
                            }, 2000);
                        }
                    }
                }, 1500);
            });
            //Faq Page Email url changes Ends Here
            // ----recent blog----
            if ($(window).width() < 768) {
                $(".blog-widget-recent").addClass("mobile-carousel");

                var blog_recent = $('.blog-widget-recent.mobile-carousel .post-list');

                blog_recent.owlCarousel({
                    items: 4,
                    slideSpeed: 800,
                    nav: true,
                    dots: false,
                    loop: true,
                    responsive: {
                        // breakpoint from 0 up
                        0: {
                            items: 1,
                            nav: false,
                            dots: false
                        },
                        // breakpoint from 481 up
                        480: {
                            items: 2,
                            nav: false,
                            dots: false
                        },
                        // breakpoint from 481 up
                        640: {
                            items: 3,
                            nav: false,
                            dots: false
                        }
                    }
                });
            }
            // ----recent blog----
            //--- Topmenu phtml js code updated
            if ($(window).width() > 767) {
                $(document).on('click', '.nav-item.level0 > .nav-anchor', function (e) {
                    if ($(this).hasClass("actived")) {
                        $(".submenu.dropdown-menu").addClass('trans_menu');
                        if ($(window).width() < 768) {
                            return true;
                        }
                        var obj = $(this).parents(".nav-item").eq(0);
                        if ($(obj).children('.submenu').length > 0 && !$(obj).hasClass("subgroup")) {
                            e.preventDefault();
                            $(this).removeClass("actived");
                            if ($(obj).hasClass('current')) {
                                $(obj).removeClass('current');
                            }
                            if ($(obj).data('caret')) {
                                $(obj).children('.nav-anchor').find('.ves-caret').removeClass($(obj).data('hovercaret')).addClass($(obj).data('caret'));
                            }

                            var container = $(" .nav-item.level0.current");
                            var container1 = $(" .nav-item.level1.current");
                            var container2 = $(" .nav-item.level2.current");
                            var container3 = $(" .nav-item.level3.current");
                            // if the target of the click isn't the container nor a descendant of the container

                            if (!container.is(e.target) && container.has(e.target).length === 0) {
                                $(container).removeClass('current');
                                return false;
                            }
                            if (!container1.is(e.target) && container1.has(e.target).length === 0) {
                                $(container1).removeClass('current');
                                return false;
                            }
                            if (!container2.is(e.target) && container2.has(e.target).length === 0) {
                                $(container2).removeClass('current');
                                return false;
                            }
                            if (!container3.is(e.target) && container3.has(e.target).length === 0) {
                                $(container3).removeClass('current');
                                return false;
                            }
                            return false;
                        }
                    } else {
                        var obj = $(this).parents(".nav-item").eq(0);
                        if ($(obj).children('.submenu').length > 0 && !$(obj).hasClass("subgroup")) {
                            if ($(obj).hasClass('level0')) {
                                $(' > .nav-item').removeClass('current');
                            }
                            if ($(obj).hasClass('current')) {
                                $(obj).removeClass('current');
                                $(".submenu.dropdown-menu").addClass('trans_menu');
                            } else {
                                $(obj).addClass('current');
                                $(this).addClass("actived");
                                $(".submenu.dropdown-menu").removeClass('trans_menu');
                            }

                            if ($(obj).data('hovericon')) {
                                $(obj).children('.nav-anchor').find('.item-icon').attr('src', $(obj).data('hovericon'));
                            }
                            if ($(obj).data('caret') && $(obj).data('hovercaret')) {
                                $(obj).children('.nav-anchor').find('.ves-caret').removeClass($(obj).data('caret')).addClass($(obj).data('hovercaret'));
                            }

                            return false;
                        }
                    }
                });
                $("li.nav-item.level0").hover(
                    function () {
                        $(this).siblings().addClass("hover-dark-transition");
                    },
                    function () {
                        $(this).siblings().removeClass("hover-dark-transition");
                    }
                );
            }
            if ($(window).width() < 767) {
                $(document).on('click', '.nav-item > .nav-anchor', function (e) {
                    if ($(this).hasClass("nav-anchor")) {
                        if ($(window).width() < 768) {
                            return true;
                        }
                        var obj = $(this).parents(".nav-item").eq(0);
                        if ($(obj).children('.submenu').length > 0 && !$(obj).hasClass("subgroup")) {
                            e.preventDefault();
                            $(this).removeClass("actived");
                            if ($(obj).hasClass('current')) {
                                $(obj).removeClass('current');
                            }
                            if ($(obj).data('caret')) {
                                $(obj).children('.nav-anchor').find('.ves-caret').removeClass($(obj).data('hovercaret')).addClass($(obj).data('caret'));
                            }

                            var container = $(" .nav-item.level0.current");
                            var container1 = $(" .nav-item.level1.current");
                            var container2 = $(" .nav-item.level2.current");
                            var container3 = $(" .nav-item.level3.current");
                            // if the target of the click isn't the container nor a descendant of the container

                            if (!container.is(e.target) && container.has(e.target).length === 0) {
                                $(container).removeClass('current');
                                return false;
                            }

                            $('.swatch-option.text').click(function () {
                                $('.swatch-option.text').removeClass('error');
                                $('.swatch-error-notice').hide();
                            });
                            if (!container1.is(e.target) && container1.has(e.target).length === 0) {
                                $(container1).removeClass('current');
                                return false;
                            }
                            if (!container2.is(e.target) && container2.has(e.target).length === 0) {
                                $(container2).removeClass('current');
                                return false;
                            }
                            if (!container3.is(e.target) && container3.has(e.target).length === 0) {
                                $(container3).removeClass('current');
                                return false;
                            }
                            return false;
                        }
                    } else {
                        var obj = $(this).parents(".nav-item").eq(0);
                        if ($(obj).children('.submenu').length > 0 && !$(obj).hasClass("subgroup")) {
                            if ($(obj).hasClass('level0')) {
                                $(' > .nav-item').removeClass('current');
                            }
                            if ($(obj).hasClass('current')) {
                                $(obj).removeClass('current');
                            } else {
                                $(obj).addClass('current');
                                $(this).addClass("actived");
                            }

                            if ($(obj).data('hovericon')) {
                                $(obj).children('.nav-anchor').find('.item-icon').attr('src', $(obj).data('hovericon'));
                            }
                            if ($(obj).data('caret') && $(obj).data('hovercaret')) {
                                $(obj).children('.nav-anchor').find('.ves-caret').removeClass($(obj).data('caret')).addClass($(obj).data('hovercaret'));
                            }

                            return false;
                        }
                    }
                });
            }

            $(".menu_close").on("click", function () {
                $(".nav-item.ui-menu-item").removeClass("current");
            });

        },

        initCheckoutPage: function () {

            if ($('body.checkout-index-index ').length) {
                $(document).ready(function() {
                    $('body').on('focusin', 'input[name="telephone"]', function () {
                        $(this).parent().find('.field-error').css("display", "none");
                    });
                    $('body').on('focusin', 'input[name="postcode"]', function () {
                        $(this).parent().find('.field-error').css("display", "none");
                    });
                    $('body').on('focusout', 'input[name="telephone"]', function () {
                        $(this).parent().find('.field-error').css("display", "block");
                    });
                    $('body').on('focusout', 'input[name="postcode"]', function () {
                        $(this).parent().find('.field-error').css("display", "block");
                    });
                    $(document).on("click","#cc-bank-accordion .List-wrap",function() {
                        $('#cc-bank-accordion .List-wrap').removeClass('allow active');
                        $("#cc-bank-accordion .List-wrap-content").hide();
                        $(this).addClass('allow active');
                        $(this).next(".List-wrap-content").show();
                    });
                });
            }
        },


    });

    return $.planetsports.main;

});

/**
 * 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);
    };
});

define('Amasty_Gdpr/js/popup',[
    'jquery',
    'Magento_Ui/js/modal/modal',
    'mage/translate'
], function ($, modal, $t) {
    'use strict';

    function getPopupData(textUrl, consentId)
    {
        return $.ajax({
            async: false,
            url: textUrl,
            cache: true,
            data: { consent_id: consentId }
        });
    }

    return function (config, element) {
        config.buttons = [
            {
                text: $t('I have read and accept'),
                'class': 'action action-primary',
                click: function () {
                    var checkbox = $($('#amgdpr-privacy-popup').data('amgdpr-checkbox-selector'));
                    checkbox.prop('checked', true);
                    checkbox.trigger('change');
                    this.closeModal();
                }
            }
        ];
        config.focus = '.action-primary';

        $(document).on('click', '[data-role="amasty-gdpr-consent"] a[href="#"]',function (e) {
            var targetCheckbox = $(this).closest('div[data-role="amasty-gdpr-consent"]').find('input[type="checkbox"]');
            e.preventDefault();
            e.stopPropagation();
            getPopupData(config.textUrl, targetCheckbox.data('consent-id')).done(function (response) {
                config.title = response.title;
                var popup = modal(config, element);
                popup.element.html(response.content);
                popup.openModal().on('modalclosed', function () {
                    popup.element.html('');
                });
                $('#amgdpr-privacy-popup').closest('.modal-popup').css('z-index', 100001);
                $('#amgdpr-privacy-popup').data('amgdpr-checkbox-selector', '#' + targetCheckbox.attr('id'));
                $('.modals-overlay').css('z-index', 100000);
            });
        });
    };
});

define('WeltPixel_DesignElements/js/btt_button',['jquery'], function ($) {
    "use strict";

    var SEMICOLONBTTBUTTON = SEMICOLONBTTBUTTON || {};

    SEMICOLONBTTBUTTON.widget = {
        init: function (options) {
            this.options = $.parseJSON(options);
            SEMICOLONBTTBUTTON.widget.btt_button();
        },

        btt_button: function(){
            var backToTop = $('.btt-button'),
                offset = this.options.offset,
                offsetOpacity = this.options.offsetOpacity,
                scrollTopDuration = this.options.scrollTopDuration;

            /** hide or show the "back to top" link */
            $(window).scroll(function(){
                ( $(this).scrollTop() > offset ) ? backToTop.addClass('cd-is-visible') : backToTop.removeClass('cd-is-visible cd-fade-out');
                if( $(this).scrollTop() > offsetOpacity ) {
                    backToTop.addClass('cd-fade-out');
                }
            });

            /** smooth scroll to top */
            backToTop.on('click', function(event){
                event.preventDefault();
                $('body,html').animate({
                        scrollTop: 0 ,
                    }, scrollTopDuration
                );
            });
        }
    };

    return SEMICOLONBTTBUTTON;
});

/**
 * 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');
        }
    };
});

(function(root) {
define("Ves_All/lib/bootstrap/js/bootstrap.min", ["jquery"], function() {
  return (function() {
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(){"use strict";var t=jQuery.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1==t[0]&&9==t[1]&&t[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(),function(o){"use strict";o.fn.emulateTransitionEnd=function(t){var e=!1,i=this;o(this).one("bsTransitionEnd",function(){e=!0});return setTimeout(function(){e||o(i).trigger(o.support.transition.end)},t),this},o(function(){o.support.transition=function(){var t,e=document.createElement("bootstrap"),i={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(t in i)if(void 0!==e.style[t])return{end:i[t]};return!1}(),o.support.transition&&(o.event.special.bsTransitionEnd={bindType:o.support.transition.end,delegateType:o.support.transition.end,handle:function(t){return o(t.target).is(this)?t.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),function(s){"use strict";function a(t){s(t).on("click",e,this.close)}var e='[data-dismiss="alert"]';a.VERSION="3.3.5",a.TRANSITION_DURATION=150,a.prototype.close=function(t){function e(){n.detach().trigger("closed.bs.alert").remove()}var i=s(this),o=i.attr("data-target");o||(o=(o=i.attr("href"))&&o.replace(/.*(?=#[^\s]*$)/,""));var n=s(o);t&&t.preventDefault(),n.length||(n=i.closest(".alert")),n.trigger(t=s.Event("close.bs.alert")),t.isDefaultPrevented()||(n.removeClass("in"),s.support.transition&&n.hasClass("fade")?n.one("bsTransitionEnd",e).emulateTransitionEnd(a.TRANSITION_DURATION):e())};var t=s.fn.alert;s.fn.alert=function(i){return this.each(function(){var t=s(this),e=t.data("bs.alert");e||t.data("bs.alert",e=new a(this)),"string"==typeof i&&e[i].call(t)})},s.fn.alert.Constructor=a,s.fn.alert.noConflict=function(){return s.fn.alert=t,this},s(document).on("click.bs.alert.data-api",e,a.prototype.close)}(jQuery),function(s){"use strict";function i(o){return this.each(function(){var t=s(this),e=t.data("bs.button"),i="object"==typeof o&&o;e||t.data("bs.button",e=new n(this,i)),"toggle"==o?e.toggle():o&&e.setState(o)})}var n=function(t,e){this.$element=s(t),this.options=s.extend({},n.DEFAULTS,e),this.isLoading=!1};n.VERSION="3.3.5",n.DEFAULTS={loadingText:"loading..."},n.prototype.setState=function(t){var e="disabled",i=this.$element,o=i.is("input")?"val":"html",n=i.data();t+="Text",null==n.resetText&&i.data("resetText",i[o]()),setTimeout(s.proxy(function(){i[o]((null==n[t]?this.options:n)[t]),"loadingText"==t?(this.isLoading=!0,i.addClass(e).attr(e,e)):this.isLoading&&(this.isLoading=!1,i.removeClass(e).removeAttr(e))},this),0)},n.prototype.toggle=function(){var t,e=!0,i=this.$element.closest('[data-toggle="buttons"]');i.length?("radio"==(t=this.$element.find("input")).prop("type")?(t.prop("checked")&&(e=!1),i.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==t.prop("type")&&(t.prop("checked")!==this.$element.hasClass("active")&&(e=!1),this.$element.toggleClass("active")),t.prop("checked",this.$element.hasClass("active")),e&&t.trigger("change")):(this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active"))};var t=s.fn.button;s.fn.button=i,s.fn.button.Constructor=n,s.fn.button.noConflict=function(){return s.fn.button=t,this},s(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(t){var e=s(t.target);e.hasClass("btn")||(e=e.closest(".btn")),i.call(e,"toggle"),s(t.target).is('input[type="radio"]')||s(t.target).is('input[type="checkbox"]')||t.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(t){s(t.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(t.type))})}(jQuery),function(h){"use strict";function n(n){return this.each(function(){var t=h(this),e=t.data("bs.carousel"),i=h.extend({},p.DEFAULTS,t.data(),"object"==typeof n&&n),o="string"==typeof n?n:i.slide;e||t.data("bs.carousel",e=new p(this,i)),"number"==typeof n?e.to(n):o?e[o]():i.interval&&e.pause().cycle()})}function p(t,e){this.$element=h(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=e,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",h.proxy(this.keydown,this)),"hover"!=this.options.pause||"ontouchstart"in document.documentElement||this.$element.on("mouseenter.bs.carousel",h.proxy(this.pause,this)).on("mouseleave.bs.carousel",h.proxy(this.cycle,this))}p.VERSION="3.3.5",p.TRANSITION_DURATION=600,p.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},p.prototype.keydown=function(t){if(!/input|textarea/i.test(t.target.tagName)){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()}},p.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(h.proxy(this.next,this),this.options.interval)),this},p.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},p.prototype.getItemForDirection=function(t,e){var i=this.getItemIndex(e);if(("prev"==t&&0===i||"next"==t&&i==this.$items.length-1)&&!this.options.wrap)return e;t=(i+("prev"==t?-1:1))%this.$items.length;return this.$items.eq(t)},p.prototype.to=function(t){var e=this,i=this.getItemIndex(this.$active=this.$element.find(".item.active"));return t>this.$items.length-1||t<0?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(i<t?"next":"prev",this.$items.eq(t))},p.prototype.pause=function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&h.support.transition&&(this.$element.trigger(h.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},p.prototype.next=function(){return this.sliding?void 0:this.slide("next")},p.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},p.prototype.slide=function(t,e){var i=this.$element.find(".item.active"),o=e||this.getItemForDirection(t,i),n=this.interval,s="next"==t?"left":"right",a=this;if(o.hasClass("active"))return this.sliding=!1;var r=o[0],e=h.Event("slide.bs.carousel",{relatedTarget:r,direction:s});if(this.$element.trigger(e),!e.isDefaultPrevented()){this.sliding=!0,n&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),(e=h(this.$indicators.children()[this.getItemIndex(o)]))&&e.addClass("active"));var l=h.Event("slid.bs.carousel",{relatedTarget:r,direction:s});return h.support.transition&&this.$element.hasClass("slide")?(o.addClass(t),o[0].offsetWidth,i.addClass(s),o.addClass(s),i.one("bsTransitionEnd",function(){o.removeClass([t,s].join(" ")).addClass("active"),i.removeClass(["active",s].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger(l)},0)}).emulateTransitionEnd(p.TRANSITION_DURATION)):(i.removeClass("active"),o.addClass("active"),this.sliding=!1,this.$element.trigger(l)),n&&this.cycle(),this}};var t=h.fn.carousel;function e(t){var e,i=h(this),o=h(i.attr("data-target")||(e=i.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,""));o.hasClass("carousel")&&(e=h.extend({},o.data(),i.data()),(i=i.attr("data-slide-to"))&&(e.interval=!1),n.call(o,e),i&&o.data("bs.carousel").to(i),t.preventDefault())}h.fn.carousel=n,h.fn.carousel.Constructor=p,h.fn.carousel.noConflict=function(){return h.fn.carousel=t,this},h(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),h(window).on("load",function(){h('[data-ride="carousel"]').each(function(){var t=h(this);n.call(t,t.data())})})}(jQuery),function(n){"use strict";function i(t){var e=t.attr("data-target")||(e=t.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"");return n(e)}function s(o){return this.each(function(){var t=n(this),e=t.data("bs.collapse"),i=n.extend({},a.DEFAULTS,t.data(),"object"==typeof o&&o);!e&&i.toggle&&/show|hide/.test(o)&&(i.toggle=!1),e||t.data("bs.collapse",e=new a(this,i)),"string"==typeof o&&e[o]()})}var a=function(t,e){this.$element=n(t),this.options=n.extend({},a.DEFAULTS,e),this.$trigger=n('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};a.VERSION="3.3.5",a.TRANSITION_DURATION=350,a.DEFAULTS={toggle:!0},a.prototype.dimension=function(){return this.$element.hasClass("width")?"width":"height"},a.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var t=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(t&&t.length&&((o=t.data("bs.collapse"))&&o.transitioning))){var e=n.Event("show.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){t&&t.length&&(s.call(t,"hide"),o||t.data("bs.collapse",null));var i=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[i](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var o=function(){this.$element.removeClass("collapsing").addClass("collapse in")[i](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!n.support.transition)return o.call(this);t=n.camelCase(["scroll",i].join("-"));this.$element.one("bsTransitionEnd",n.proxy(o,this)).emulateTransitionEnd(a.TRANSITION_DURATION)[i](this.$element[0][t])}}}},a.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var t=n.Event("hide.bs.collapse");if(this.$element.trigger(t),!t.isDefaultPrevented()){var e=this.dimension();this.$element[e](this.$element[e]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;t=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return n.support.transition?void this.$element[e](0).one("bsTransitionEnd",n.proxy(t,this)).emulateTransitionEnd(a.TRANSITION_DURATION):t.call(this)}}},a.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},a.prototype.getParent=function(){return n(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(n.proxy(function(t,e){e=n(e);this.addAriaAndCollapsedClass(i(e),e)},this)).end()},a.prototype.addAriaAndCollapsedClass=function(t,e){var i=t.hasClass("in");t.attr("aria-expanded",i),e.toggleClass("collapsed",!i).attr("aria-expanded",i)};var t=n.fn.collapse;n.fn.collapse=s,n.fn.collapse.Constructor=a,n.fn.collapse.noConflict=function(){return n.fn.collapse=t,this},n(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(t){var e=n(this);e.attr("data-target")||t.preventDefault();t=i(e),e=t.data("bs.collapse")?"toggle":e.data();s.call(t,e)})}(jQuery),function(n){"use strict";function s(t){var e=t.attr("data-target");e||(e=(e=t.attr("href"))&&/#[A-Za-z]/.test(e)&&e.replace(/.*(?=#[^\s]*$)/,""));e=e&&n(e);return e&&e.length?e:t.parent()}function a(o){o&&3===o.which||(n(".dropdown-backdrop").remove(),n(r).each(function(){var t=n(this),e=s(t),i={relatedTarget:this};e.hasClass("open")&&(o&&"click"==o.type&&/input|textarea/i.test(o.target.tagName)&&n.contains(e[0],o.target)||(e.trigger(o=n.Event("hide.bs.dropdown",i)),o.isDefaultPrevented()||(t.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",i))))}))}function o(t){n(t).on("click.bs.dropdown",this.toggle)}var r='[data-toggle="dropdown"]';o.VERSION="3.3.5",o.prototype.toggle=function(t){var e=n(this);if(!e.is(".disabled, :disabled")){var i=s(e),o=i.hasClass("open");if(a(),!o){"ontouchstart"in document.documentElement&&!i.closest(".navbar-nav").length&&n(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(n(this)).on("click",a);o={relatedTarget:this};if(i.trigger(t=n.Event("show.bs.dropdown",o)),t.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),i.toggleClass("open").trigger("shown.bs.dropdown",o)}return!1}},o.prototype.keydown=function(t){if(/(38|40|27|32)/.test(t.which)&&!/input|textarea/i.test(t.target.tagName)){var e=n(this);if(t.preventDefault(),t.stopPropagation(),!e.is(".disabled, :disabled")){var i=s(e),o=i.hasClass("open");if(!o&&27!=t.which||o&&27==t.which)return 27==t.which&&i.find(r).trigger("focus"),e.trigger("click");e=i.find(".dropdown-menu li:not(.disabled):visible a");e.length&&(i=e.index(t.target),38==t.which&&0<i&&i--,40==t.which&&i<e.length-1&&i++,~i||(i=0),e.eq(i).trigger("focus"))}}};var t=n.fn.dropdown;n.fn.dropdown=function(i){return this.each(function(){var t=n(this),e=t.data("bs.dropdown");e||t.data("bs.dropdown",e=new o(this)),"string"==typeof i&&e[i].call(t)})},n.fn.dropdown.Constructor=o,n.fn.dropdown.noConflict=function(){return n.fn.dropdown=t,this},n(document).on("click.bs.dropdown.data-api",a).on("click.bs.dropdown.data-api",".dropdown form",function(t){t.stopPropagation()}).on("click.bs.dropdown.data-api",r,o.prototype.toggle).on("keydown.bs.dropdown.data-api",r,o.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",o.prototype.keydown)}(jQuery),function(l){"use strict";function h(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)}h.VERSION="3.3.5",h.TRANSITION_DURATION=150,h.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},h.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=l(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&l(l.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s,a=o[n];"click"==a?this.$element.on("click."+this.type,this.options.selector,l.proxy(this.toggle,this)):"manual"!=a&&(s="hover"==a?"mouseenter":"focusin",a="hover"==a?"mouseleave":"focusout",this.$element.on(s+"."+this.type,this.options.selector,l.proxy(this.enter,this)),this.$element.on(a+"."+this.type,this.options.selector,l.proxy(this.leave,this)))}this.options.selector?this._options=l.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},h.prototype.getDefaults=function(){return h.DEFAULTS},h.prototype.getOptions=function(t){return(t=l.extend({},this.getDefaults(),this.$element.data(),t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t},h.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&l.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},h.prototype.enter=function(t){var e=t instanceof this.constructor?t:l(t.currentTarget).data("bs."+this.type);return e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),l(t.currentTarget).data("bs."+this.type,e)),t instanceof l.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState?void(e.hoverState="in"):(clearTimeout(e.timeout),e.hoverState="in",e.options.delay&&e.options.delay.show?void(e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)):e.show())},h.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},h.prototype.leave=function(t){var e=t instanceof this.constructor?t:l(t.currentTarget).data("bs."+this.type);return e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),l(t.currentTarget).data("bs."+this.type,e)),t instanceof l.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),e.isInStateTrue()?void 0:(clearTimeout(e.timeout),e.hoverState="out",e.options.delay&&e.options.delay.hide?void(e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)):e.hide())},h.prototype.show=function(){var e,t,i,o,n,s,a,r=l.Event("show.bs."+this.type);this.hasContent()&&this.enabled&&(this.$element.trigger(r),i=l.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]),!r.isDefaultPrevented()&&i&&(t=(e=this).tip(),s=this.getUID(this.type),this.setContent(),t.attr("id",s),this.$element.attr("aria-describedby",s),this.options.animation&&t.addClass("fade"),a="function"==typeof this.options.placement?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,(n=(o=/\s?auto?\s?/i).test(a))&&(a=a.replace(o,"")||"top"),t.detach().css({top:0,left:0,display:"block"}).addClass(a).data("bs."+this.type,this),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type),r=this.getPosition(),i=t[0].offsetWidth,s=t[0].offsetHeight,n&&(o=a,n=this.getPosition(this.$viewport),a="bottom"==a&&r.bottom+s>n.bottom?"top":"top"==a&&r.top-s<n.top?"bottom":"right"==a&&r.right+i>n.width?"left":"left"==a&&r.left-i<n.left?"right":a,t.removeClass(o).addClass(a)),s=this.getCalculatedOffset(a,r,i,s),this.applyPlacement(s,a),a=function(){var t=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==t&&e.leave(e)},l.support.transition&&this.$tip.hasClass("fade")?t.one("bsTransitionEnd",a).emulateTransitionEnd(h.TRANSITION_DURATION):a()))},h.prototype.applyPlacement=function(t,e){var i=this.tip(),o=i[0].offsetWidth,n=i[0].offsetHeight,s=parseInt(i.css("margin-top"),10),a=parseInt(i.css("margin-left"),10);isNaN(s)&&(s=0),isNaN(a)&&(a=0),t.top+=s,t.left+=a,l.offset.setOffset(i[0],l.extend({using:function(t){i.css({top:Math.round(t.top),left:Math.round(t.left)})}},t),0),i.addClass("in");var r=i[0].offsetWidth,s=i[0].offsetHeight;"top"==e&&s!=n&&(t.top=t.top+n-s);a=this.getViewportAdjustedDelta(e,t,r,s);a.left?t.left+=a.left:t.top+=a.top;e=/top|bottom/.test(e),n=e?2*a.left-o+r:2*a.top-n+s,s=e?"offsetWidth":"offsetHeight";i.offset(t),this.replaceArrow(n,i[0][s],e)},h.prototype.replaceArrow=function(t,e,i){this.arrow().css(i?"left":"top",50*(1-t/e)+"%").css(i?"top":"left","")},h.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();t.find(".tooltip-inner")[this.options.html?"html":"text"](e),t.removeClass("fade in top bottom left right")},h.prototype.hide=function(t){function e(){"in"!=i.hoverState&&o.detach(),i.$element.removeAttr("aria-describedby").trigger("hidden.bs."+i.type),t&&t()}var i=this,o=l(this.$tip),n=l.Event("hide.bs."+this.type);return this.$element.trigger(n),n.isDefaultPrevented()?void 0:(o.removeClass("in"),l.support.transition&&o.hasClass("fade")?o.one("bsTransitionEnd",e).emulateTransitionEnd(h.TRANSITION_DURATION):e(),this.hoverState=null,this)},h.prototype.fixTitle=function(){var t=this.$element;!t.attr("title")&&"string"==typeof t.attr("data-original-title")||t.attr("data-original-title",t.attr("title")||"").attr("title","")},h.prototype.hasContent=function(){return this.getTitle()},h.prototype.getPosition=function(t){var e=(t=t||this.$element)[0],i="BODY"==e.tagName,o=e.getBoundingClientRect();null==o.width&&(o=l.extend({},o,{width:o.right-o.left,height:o.bottom-o.top}));e=i?{top:0,left:0}:t.offset(),t={scroll:i?document.documentElement.scrollTop||document.body.scrollTop:t.scrollTop()},i=i?{width:l(window).width(),height:l(window).height()}:null;return l.extend({},o,t,i,e)},h.prototype.getCalculatedOffset=function(t,e,i,o){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-i/2}:"top"==t?{top:e.top-o,left:e.left+e.width/2-i/2}:"left"==t?{top:e.top+e.height/2-o/2,left:e.left-i}:{top:e.top+e.height/2-o/2,left:e.left+e.width}},h.prototype.getViewportAdjustedDelta=function(t,e,i,o){var n={top:0,left:0};if(!this.$viewport)return n;var s,a=this.options.viewport&&this.options.viewport.padding||0,r=this.getPosition(this.$viewport);return/right|left/.test(t)?(t=e.top-a-r.scroll,s=e.top+a-r.scroll+o,t<r.top?n.top=r.top-t:s>r.top+r.height&&(n.top=r.top+r.height-s)):(s=e.left-a,i=e.left+a+i,s<r.left?n.left=r.left-s:i>r.right&&(n.left=r.left+r.width-i)),n},h.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},h.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},h.prototype.tip=function(){if(!this.$tip&&(this.$tip=l(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},h.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},h.prototype.enable=function(){this.enabled=!0},h.prototype.disable=function(){this.enabled=!1},h.prototype.toggleEnabled=function(){this.enabled=!this.enabled},h.prototype.toggle=function(t){var e=this;t&&((e=l(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),l(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},h.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null})};var t=l.fn.tooltip;l.fn.tooltip=function(o){return this.each(function(){var t=l(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new h(this,i)),"string"==typeof o&&e[o]())})},l.fn.tooltip.Constructor=h,l.fn.tooltip.noConflict=function(){return l.fn.tooltip=t,this}}(jQuery),function(n){"use strict";function s(t,e){this.init("popover",t,e)}if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.3.5",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof i?"html":"append":"text"](i),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(n){"use strict";function s(t,e){this.$body=n(document.body),this.$scrollElement=n(n(t).is(document.body)?window:t),this.options=n.extend({},s.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",n.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=n(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new s(this,i)),"string"==typeof o&&e[o]()})}s.VERSION="3.3.5",s.DEFAULTS={offset:10},s.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},s.prototype.refresh=function(){var t=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),n.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=n(this),e=t.data("target")||t.attr("href"),t=/^#./.test(e)&&n(e);return t&&t.length&&t.is(":visible")?[[t[i]().top+o,e]]:null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},s.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e<n[0])return this.activeTarget=null,this.clear();for(t=n.length;t--;)a!=s[t]&&e>=n[t]&&(void 0===n[t+1]||e<n[t+1])&&this.activate(s[t])},s.prototype.activate=function(t){this.activeTarget=t,this.clear();t=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',t=n(t).parents("li").addClass("active");t.parent(".dropdown-menu").length&&(t=t.closest("li.dropdown").addClass("active")),t.trigger("activate.bs.scrollspy")},s.prototype.clear=function(){n(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=n.fn.scrollspy;n.fn.scrollspy=e,n.fn.scrollspy.Constructor=s,n.fn.scrollspy.noConflict=function(){return n.fn.scrollspy=t,this},n(window).on("load.bs.scrollspy.data-api",function(){n('[data-spy="scroll"]').each(function(){var t=n(this);e.call(t,t.data())})})}(jQuery),function(a){"use strict";function e(i){return this.each(function(){var t=a(this),e=t.data("bs.tab");e||t.data("bs.tab",e=new r(this)),"string"==typeof i&&e[i]()})}function r(t){this.element=a(t)}r.VERSION="3.3.5",r.TRANSITION_DURATION=150,r.prototype.show=function(){var t,e,i,o=this.element,n=o.closest("ul:not(.dropdown-menu)"),s=o.data("target");s||(s=(s=o.attr("href"))&&s.replace(/.*(?=#[^\s]*$)/,"")),o.parent("li").hasClass("active")||(t=n.find(".active:last a"),e=a.Event("hide.bs.tab",{relatedTarget:o[0]}),i=a.Event("show.bs.tab",{relatedTarget:t[0]}),t.trigger(e),o.trigger(i),i.isDefaultPrevented()||e.isDefaultPrevented()||(s=a(s),this.activate(o.closest("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"hidden.bs.tab",relatedTarget:o[0]}),o.trigger({type:"shown.bs.tab",relatedTarget:t[0]})})))},r.prototype.activate=function(t,e,i){function o(){n.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}var n=e.find("> .active"),s=i&&a.support.transition&&(n.length&&n.hasClass("fade")||!!e.find("> .fade").length);n.length&&s?n.one("bsTransitionEnd",o).emulateTransitionEnd(r.TRANSITION_DURATION):o(),n.removeClass("in")};var t=a.fn.tab;function i(t){t.preventDefault(),e.call(a(this),"show")}a.fn.tab=e,a.fn.tab.Constructor=r,a.fn.tab.noConflict=function(){return a.fn.tab=t,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(a){"use strict";function i(o){return this.each(function(){var t=a(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new r(this,i)),"string"==typeof o&&e[o]()})}var r=function(t,e){this.options=a.extend({},r.DEFAULTS,e),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};r.VERSION="3.3.5",r.RESET="affix affix-top affix-bottom",r.DEFAULTS={offset:0,target:window},r.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n<i&&"top";if("bottom"==this.affixed)return null!=i?!(n+this.unpin<=s.top)&&"bottom":!(n+a<=t-o)&&"bottom";var r=null==this.affixed,s=r?n:s.top;return null!=i&&n<=i?"top":null!=o&&t-o<=s+(r?a:e)&&"bottom"},r.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(r.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},r.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},r.prototype.checkPosition=function(){if(this.$element.is(":visible")){var t=this.$element.height(),e=this.options.offset,i=e.top,o=e.bottom,n=Math.max(a(document).height(),a(document.body).height());"object"!=typeof e&&(o=i=e),"function"==typeof i&&(i=e.top(this.$element)),"function"==typeof o&&(o=e.bottom(this.$element));var s=this.getState(n,t,i,o);if(this.affixed!=s){null!=this.unpin&&this.$element.css("top","");e="affix"+(s?"-"+s:""),i=a.Event(e+".bs.affix");if(this.$element.trigger(i),i.isDefaultPrevented())return;this.affixed=s,this.unpin="bottom"==s?this.getPinnedOffset():null,this.$element.removeClass(r.RESET).addClass(e).trigger(e.replace("affix","affixed")+".bs.affix")}"bottom"==s&&this.$element.offset({top:n-t-o})}};var t=a.fn.affix;a.fn.affix=i,a.fn.affix.Constructor=r,a.fn.affix.noConflict=function(){return a.fn.affix=t,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var t=a(this),e=t.data();e.offset=e.offset||{},null!=e.offsetBottom&&(e.offset.bottom=e.offsetBottom),null!=e.offsetTop&&(e.offset.top=e.offsetTop),i.call(t,e)})})}(jQuery);

  }).apply(root, arguments);
});
}(this));

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

define('Magento_ReCaptchaFrontendUi/js/registry',['ko'], function (ko) {
    'use strict';

    return {
        ids: ko.observableArray([]),
        captchaList: ko.observableArray([]),
        tokenFields: ko.observableArray([])
    };
});

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

define('Magento_ReCaptchaFrontendUi/js/ui-messages-mixin',['Magento_ReCaptchaFrontendUi/js/registry'], function (registry) {
    'use strict';

    return function (originalComponent) {
        return originalComponent.extend({
            /**
             * Initialize reset on messages
             * @returns {initialize}
             */
            initialize: function () {
                this._super();

                this.messageContainer.errorMessages.subscribe(function () {
                    var
                        i,
                        captchaList = registry.captchaList(),
                        tokenFieldsList = registry.tokenFields();

                    for (i = 0; i < captchaList.length; i++) {
                        // eslint-disable-next-line no-undef
                        grecaptcha.reset(captchaList[i]);

                        if (tokenFieldsList[i]) {
                            tokenFieldsList[i].value = '';
                        }
                    }
                }, null, 'arrayChange');

                return this;
            }
        });
    };
});

define('HenriqueKieckbusch_SpeedupPages/js/speedup',['jquery'], function($) {
    'use strict';

    return function(config, element) {
        const specType = 'speculationrules';
        $(document).ready(function() {

            const urls = listElements();
            if (supportTechnologies())
            {
                const specScript = document.createElement("script");
                specScript.type = "speculationrules";
                const specRules = {
                    prerender: [
                        {
                            source: "list",
                            urls: urls,
                            eagerness: config.preload.eagerness
                        },
                    ],
                };
                specScript.textContent = JSON.stringify(specRules);
                document.body.append(specScript);
            } else {
                for(let i in urls) {
                    $("<link>", {
                        rel: "prefetch",
                        href: urls[i]
                    }).appendTo("head");
                }
            }
        });

        function supportTechnologies() {
            return HTMLScriptElement.supports && HTMLScriptElement.supports(specType);
        }

        function listElements() {
            let elements = [];
            if (config.preload.pdp) {
                elements.push(
                    'a.product'
                );
            }
            if (config.preload.category) {
                elements.push(
                    '.category-item a'
                );
            }

            if (config.preload.cms) {
                elements.push(
                    '[class*="link"] a:not([class])'
                );
            }

            if (config.preload.cart) {
                elements.push(
                    'a.showcart'
                );
            }

            const links = [];
            //foreach to guarantee the order, products first, then categories, then cms...
            for(let i in elements) {
                $(elements[i]).each(function() {
                    const href = $(this).attr('href');
                    if (href) {
                        const currentDomain = window.location.origin;
                        const isInternalLink = href.startsWith(currentDomain) || href.startsWith('/');
                        const isValidLink = !href.includes('referer') && !href.includes('logout');

                        if (isInternalLink && isValidLink) {
                            links.push(href);
                        }
                    }
                });
            }

            return links;
        }
    };
});

define('WeltPixel_ProductLabels/js/weltpixel_productlabels',[
    'jquery'
    ], function ($) {
    "use strict";

    var wpProductLabels = {
        init: function(requestUrl) {
            if (!requestUrl.length) {
                return false;
            }

            $(document).on('wpproductlabels:init', {}, function() {
                var productIds = [];
                $("[data-wpproductlabel='1']").each(function() {
                    productIds.push($(this).attr('data-product-id'));
                });

                if (productIds.length) {
                    $.ajax({
                        url: requestUrl,
                        method: 'POST',
                        cache: false,
                        global: false,
                        data: {
                            product_ids: productIds
                        },
                        success: function (result) {
                            for (var i in result) {
                                var resultObj = result[i];
                                var prId = resultObj.productId;
                                var prHtml = resultObj.html;
                                $("[data-product-id='"+prId+"']").attr("data-wpproductlabel", 0).find('img').after(prHtml.imagePosition);
                            }
                        }
                    });
                }
            });
        },

        /** @Deprecated Not used anymore */
        adjustRightLabels: function() {
            var verticalThumbs = $('.fotorama__nav-wrap--vertical');
            var adjustmentElements = $('.wp-product-label.wp-product-label-top-right, .wp-product-label.wp-product-label-middle-right, .wp-product-label.wp-product-label-bottom-right');
            if (verticalThumbs.length) {
                var verticalThumbWidth = verticalThumbs.width() + 5;
                var adjustmentElements = $('.wp-product-label.wp-product-label-top-right, .wp-product-label.wp-product-label-middle-right, .wp-product-label.wp-product-label-bottom-right');
                adjustmentElements.css('margin-right', verticalThumbWidth + 'px')
            } else {
                adjustmentElements.css('margin-right', 0)
            }
        }
    };

    return wpProductLabels;
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Mageants_SpamAndBotBlocker/js/view/messages',[
    'jquery',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'underscore',
    'escaper',
    'jquery/jquery-storageapi'
], function ($,uiComponent,customerData, _, escaper) {
    'use strict';

    /**
    * Return the UI Component
    */
    return uiComponent.extend({

        defaults: {
            cookieMessages: [],
            messages: [],
            allowedTags: ['div', 'span', 'b', 'strong', 'i', 'em', 'u', 'a']
        },
        /**
         * Initialization method
         */
        initialize: function () {
            this._super();

            this.cookieMessages = _.unique($.cookieStorage.get('mage-messages'), 'text');
            this.messages = customerData.get('messages').extend({
                disposableCustomerData: 'messages'
            });

            // Force to clean obsolete messages
            if (!_.isEmpty(this.messages().messages)) {
                customerData.set('messages', {});
            }

            $.mage.cookies.set('mage-messages', '', {
                samesite: 'strict',
                domain: '',
                path: '/'
            });
        },

        /**
         * Prepare the given message to be rendered as HTML
         *
         * @param {String} message
         * @return {String}
         */
        prepareMessageForHtml: function (message) {
            return escaper.escapeHtml(message, this.allowedTags);
        }
    });
});

define('Magento_Theme/js/lib/mage/validation-mixin',[
    'jquery'
], function ($) {
    'use strict';
    return function (widget) {
        $.widget('mage.validation', widget, {
            /**
             * Handle form validation. Focus on first invalid form field.
             *
             * @param {jQuery.Event} event
             * @param {Object} validation
             */
            listenFormValidateHandler: function (event, validation) {
                let firstActive = $(validation.errorList[0].element || []),
                    lastActive = $(validation.findLastActive() ||
                        validation.errorList.length && validation.errorList[0].element || []),
                    parent, windowHeight, successList;

                if (lastActive.is(':hidden')) {
                    parent = lastActive.parent();
                    windowHeight = $(window).height();
                    $('html, body').animate({
                        scrollTop: parent.offset().top - windowHeight / 2
                    });
                }

                // ARIA (removing aria attributes if success)
                successList = validation.successList;

                if (successList.length) {
                    $.each(successList, function () {
                        $(this)
                            .removeAttr('aria-describedby')
                            .removeAttr('aria-invalid');
                    });
                }

                // if (firstActive.length) {
                //     /* vertically center the first field on the screen. This is best for UX but if you prefer to avoid the scrolling completelly,
                //     just remove the next line and the function "scrollToCenterFormFieldOnError" below. */
                //     scrollToCenterFormFieldOnError(firstActive);
                //     firstActive.focus();
                // }
            }
        });
        function scrollToCenterFormFieldOnError(firstActive) {
            let fieldTop = firstActive.offset().top,
                fieldHeight = firstActive.height(),
                windowHeight = $(window).height(),
                offset;

            offset = fieldTop - ((windowHeight / 2) - (fieldHeight / 2));

            $('html, body').stop().animate({
                scrollTop: offset
            });
        }
        return $.mage.validation;
    }
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define('mage/loader',[
    'jquery',
    'mage/template',
    'jquery-ui-modules/widget',
    'mage/translate'
], function ($, mageTemplate) {
    'use strict';

    $.widget('mage.loader', {
        loaderStarted: 0,
        options: {
            icon: '',
            texts: {
                loaderText: $.mage.__('Please wait...'),
                imgAlt: $.mage.__('Loading...')
            },
            template:
                '<div class="loading-mask" data-role="loader">' +
                    '<div class="loader">' +
                        '<img alt="<%- data.texts.imgAlt %>" src="<%- data.icon %>">' +
                        '<p><%- data.texts.loaderText %></p>' +
                    '</div>' +
                '</div>'

        },

        /**
         * 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._render();
            this.loaderStarted++;
            this.spinner.show();

            if (ctx) {
                this.spinner
                    .css({
                        width: ctx.outerWidth(),
                        height: ctx.outerHeight(),
                        position: 'absolute'
                    })
                    .position({
                        my: 'top left',
                        at: 'top left',
                        of: ctx
                    });
            }

            return false;
        },

        /**
         * Hide loader
         */
        hide: function () {
            if (this.loaderStarted > 0) {
                this.loaderStarted--;

                if (this.loaderStarted === 0) {
                    this.spinner.hide();
                }
            }

            return false;
        },

        /**
         * Render loader
         * @protected
         */
        _render: function () {
            var html;

            if (!this.spinnerTemplate) {
                this.spinnerTemplate = mageTemplate(this.options.template);

                html = $(this.spinnerTemplate({
                    data: this.options
                }));

                html.prependTo(this.element);

                this.spinner = html;
            }
        },

        /**
         * 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('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);
    };
});

/* ========================================================================
 * Bootstrap: alert.js v3.3.6
 * http://getbootstrap.com/javascript/#alerts
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */
define('WeltPixel_DesignElements/js/bootstrap/alert',["jquery"], // Require jquery
    function($){

+function ($) {
    'use strict';

    // ALERT CLASS DEFINITION
    // ======================

    var dismiss = '[data-dismiss="alert"]'
    var Alert   = function (el) {
        $(el).on('click', dismiss, this.close)
    }

    Alert.VERSION = '3.3.6'

    Alert.TRANSITION_DURATION = 150

    Alert.prototype.close = function (e) {
        var $this    = $(this)
        var selector = $this.attr('data-target')

        if (!selector) {
            selector = $this.attr('href')
            selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
        }

        var $parent = $(selector)

        if (e) e.preventDefault()

        if (!$parent.length) {
            $parent = $this.closest('.alert')
        }

        $parent.trigger(e = $.Event('close.bs.alert'))

        if (e.isDefaultPrevented()) return

        $parent.removeClass('in')

        function removeElement() {
            // detach from parent, fire event then clean up data
            $parent.detach().trigger('closed.bs.alert').remove()
        }

        $.support.transition && $parent.hasClass('fade') ?
            $parent
                .one('bsTransitionEnd', removeElement)
                .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
            removeElement()
    }


    // ALERT PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this)
            var data  = $this.data('bs.alert')

            if (!data) $this.data('bs.alert', (data = new Alert(this)))
            if (typeof option == 'string') data[option].call($this)
        })
    }

    var old = $.fn.alert

    $.fn.alert             = Plugin
    $.fn.alert.Constructor = Alert


    // ALERT NO CONFLICT
    // =================

    $.fn.alert.noConflict = function () {
        $.fn.alert = old
        return this
    }


    // ALERT DATA-API
    // ==============
    $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)

}(jQuery);
});
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define('Magento_Ui/js/form/form',[
    'underscore',
    'Magento_Ui/js/lib/spinner',
    'rjsResolver',
    './adapter',
    'uiCollection',
    'mageUtils',
    'jquery',
    'Magento_Ui/js/core/app',
    'mage/validation'
], function (_, loader, resolver, adapter, Collection, utils, $, app) {
    'use strict';

    /**
     * Format params
     *
     * @param {Object} params
     * @returns {Array}
     */
    function prepareParams(params) {
        var result = '?';

        _.each(params, function (value, key) {
            result += key + '=' + value + '&';
        });

        return result.slice(0, -1);
    }

    /**
     * Collect form data.
     *
     * @param {Array} items
     * @returns {Object}
     */
    function collectData(items) {
        var result = {},
            name;

        items = Array.prototype.slice.call(items);

        items.forEach(function (item) {
            switch (item.type) {
                case 'checkbox':
                    result[item.name] = +!!item.checked;
                    break;

                case 'radio':
                    if (item.checked) {
                        result[item.name] = item.value;
                    }
                    break;

                case 'select-multiple':
                    name = item.name.substring(0, item.name.length - 2); //remove [] from the name ending
                    result[name] = _.pluck(item.selectedOptions, 'value');
                    break;

                default:
                    result[item.name] = item.value;
            }
        });

        return result;
    }

    /**
     * Makes ajax request
     *
     * @param {Object} params
     * @param {Object} data
     * @param {String} url
     * @returns {*}
     */
    function makeRequest(params, data, url) {
        var save = $.Deferred();

        data = utils.serialize(data);
        data['form_key'] = window.FORM_KEY;

        if (!url) {
            save.resolve();
        }

        $('body').trigger('processStart');

        $.ajax({
            url: url + prepareParams(params),
            data: data,
            dataType: 'json',

            /**
             * Success callback.
             * @param {Object} resp
             * @returns {Boolean}
             */
            success: function (resp) {
                if (resp.ajaxExpired) {
                    window.location.href = resp.ajaxRedirect;
                }

                if (!resp.error) {
                    save.resolve(resp);

                    return true;
                }

                $('body').notification('clear');
                $.each(resp.messages, function (key, message) {
                    $('body').notification('add', {
                        error: resp.error,
                        message: message,

                        /**
                         * Inserts message on page
                         * @param {String} msg
                         */
                        insertMethod: function (msg) {
                            $('.page-main-actions').after(msg);
                        }
                    });
                });
            },

            /**
             * Complete callback.
             */
            complete: function () {
                $('body').trigger('processStop');
            }
        });

        return save.promise();
    }

    /**
     * Check if fields is valid.
     *
     * @param {Array}items
     * @returns {Boolean}
     */
    function isValidFields(items) {
        var result = true;

        _.each(items, function (item) {
            if (!$.validator.validateSingleElement(item)) {
                result = false;
            }
        });

        return result;
    }

    return Collection.extend({
        defaults: {
            additionalFields: [],
            additionalInvalid: false,
            selectorPrefix: '.page-content',
            messagesClass: 'messages',
            errorClass: '.admin__field._error',
            eventPrefix: '.${ $.index }',
            ajaxSave: false,
            ajaxSaveType: 'default',
            imports: {
                reloadUrl: '${ $.provider}:reloadUrl'
            },
            listens: {
                selectorPrefix: 'destroyAdapter initAdapter',
                '${ $.name }.${ $.reloadItem }': 'params.set reload'
            },
            exports: {
                selectorPrefix: '${ $.provider }:client.selectorPrefix',
                messagesClass: '${ $.provider }:client.messagesClass'
            }
        },

        /** @inheritdoc */
        initialize: function () {
            this._super()
                .initAdapter();

            resolver(this.hideLoader, this);

            return this;
        },

        /** @inheritdoc */
        initObservable: function () {
            return this._super()
                .observe([
                    'responseData',
                    'responseStatus'
                ]);
        },

        /** @inheritdoc */
        initConfig: function () {
            this._super();

            this.selector = '[data-form-part=' + this.namespace + ']';

            return this;
        },

        /**
         * Initialize adapter handlers.
         *
         * @returns {Object}
         */
        initAdapter: function () {
            adapter.on({
                'reset': this.reset.bind(this),
                'save': this.save.bind(this, true, {}),
                'saveAndContinue': this.save.bind(this, false, {})
            }, this.selectorPrefix, this.eventPrefix);

            return this;
        },

        /**
         * Destroy adapter handlers.
         *
         * @returns {Object}
         */
        destroyAdapter: function () {
            adapter.off([
                'reset',
                'save',
                'saveAndContinue'
            ], this.eventPrefix);

            return this;
        },

        /**
         * Hide loader.
         *
         * @returns {Object}
         */
        hideLoader: function () {
            loader.get(this.name).hide();

            return this;
        },

        /**
         * Validate and save form.
         *
         * @param {String} redirect
         * @param {Object} data
         */
        save: function (redirect, data) {
            this.validate();

            if (!this.additionalInvalid && !this.source.get('params.invalid')) {
                this.setAdditionalData(data)
                    .submit(redirect);
            } else {
                this.focusInvalid();
            }
        },

        /**
         * Tries to set focus on first invalid form field.
         *
         * @returns {Object}
         */
        focusInvalid: function () {
            var invalidField = _.find(this.delegate('checkInvalid'));

            if (!_.isUndefined(invalidField) && _.isFunction(invalidField.focused)) {
                invalidField.focused(true);
            }

            return this;
        },

        /**
         * Set additional data to source before form submit and after validation.
         *
         * @param {Object} data
         * @returns {Object}
         */
        setAdditionalData: function (data) {
            _.each(data, function (value, name) {
                this.source.set('data.' + name, value);
            }, this);

            return this;
        },

        /**
         * Submits form
         *
         * @param {String} redirect
         */
        submit: function (redirect) {
            var additional = collectData(this.additionalFields),
                source = this.source;

            _.each(additional, function (value, name) {
                source.set('data.' + name, value);
            });

            source.save({
                redirect: redirect,
                ajaxSave: this.ajaxSave,
                ajaxSaveType: this.ajaxSaveType,
                response: {
                    data: this.responseData,
                    status: this.responseStatus
                },
                attributes: {
                    id: this.namespace
                }
            });
        },

        /**
         * Validates each element and returns true, if all elements are valid.
         */
        validate: function () {
            this.additionalFields = document.querySelectorAll(this.selector);
            this.source.set('params.invalid', false);
            this.source.trigger('data.validate');
            this.set('additionalInvalid', !isValidFields(this.additionalFields));
        },

        /**
         * Trigger reset form data.
         */
        reset: function () {
            this.source.trigger('data.reset');
            $('[data-bind*=datepicker]').val('');
        },

        /**
         * Trigger overload form data.
         */
        overload: function () {
            this.source.trigger('data.overload');
        },

        /**
         * Updates data from server.
         */
        reload: function () {
            makeRequest(this.params, this.data, this.reloadUrl).then(function (data) {
                app(data, true);
            });
        }
    });
});

(function(root) {
define("mageplaza/lazyloading/lib/lazyload", ["jquery"], function() {
  return (function() {
/*! jQuery & Zepto Lazy v1.7.10 - http://jquery.eisbehr.de/lazy - MIT&GPL-2.0 license - Copyright 2012-2018 Daniel 'Eisbehr' Kern */
!function(t,e){"use strict";function r(r,a,i,u,l){function f(){L=t.devicePixelRatio>1,i=c(i),a.delay>=0&&setTimeout(function(){s(!0)},a.delay),(a.delay<0||a.combined)&&(u.e=v(a.throttle,function(t){"resize"===t.type&&(w=B=-1),s(t.all)}),u.a=function(t){t=c(t),i.push.apply(i,t)},u.g=function(){return i=n(i).filter(function(){return!n(this).data(a.loadedName)})},u.f=function(t){for(var e=0;e<t.length;e++){var r=i.filter(function(){return this===t[e]});r.length&&s(!1,r)}},s(),n(a.appendScroll).on("scroll."+l+" resize."+l,u.e))}function c(t){var i=a.defaultImage,o=a.placeholder,u=a.imageBase,l=a.srcsetAttribute,f=a.loaderAttribute,c=a._f||{};t=n(t).filter(function(){var t=n(this),r=m(this);return!t.data(a.handledName)&&(t.attr(a.attribute)||t.attr(l)||t.attr(f)||c[r]!==e)}).data("plugin_"+a.name,r);for(var s=0,d=t.length;s<d;s++){var A=n(t[s]),g=m(t[s]),h=A.attr(a.imageBaseAttribute)||u;g===N&&h&&A.attr(l)&&A.attr(l,b(A.attr(l),h)),c[g]===e||A.attr(f)||A.attr(f,c[g]),g===N&&i&&!A.attr(E)?A.attr(E,i):g===N||!o||A.css(O)&&"none"!==A.css(O)||A.css(O,"url('"+o+"')")}return t}function s(t,e){if(!i.length)return void(a.autoDestroy&&r.destroy());for(var o=e||i,u=!1,l=a.imageBase||"",f=a.srcsetAttribute,c=a.handledName,s=0;s<o.length;s++)if(t||e||A(o[s])){var g=n(o[s]),h=m(o[s]),b=g.attr(a.attribute),v=g.attr(a.imageBaseAttribute)||l,p=g.attr(a.loaderAttribute);g.data(c)||a.visibleOnly&&!g.is(":visible")||!((b||g.attr(f))&&(h===N&&(v+b!==g.attr(E)||g.attr(f)!==g.attr(F))||h!==N&&v+b!==g.css(O))||p)||(u=!0,g.data(c,!0),d(g,h,v,p))}u&&(i=n(i).filter(function(){return!n(this).data(c)}))}function d(t,e,r,i){++z;var o=function(){y("onError",t),p(),o=n.noop};y("beforeLoad",t);var u=a.attribute,l=a.srcsetAttribute,f=a.sizesAttribute,c=a.retinaAttribute,s=a.removeAttribute,d=a.loadedName,A=t.attr(c);if(i){var g=function(){s&&t.removeAttr(a.loaderAttribute),t.data(d,!0),y(T,t),setTimeout(p,1),g=n.noop};t.off(I).one(I,o).one(D,g),y(i,t,function(e){e?(t.off(D),g()):(t.off(I),o())})||t.trigger(I)}else{var h=n(new Image);h.one(I,o).one(D,function(){t.hide(),e===N?t.attr(C,h.attr(C)).attr(F,h.attr(F)).attr(E,h.attr(E)):t.css(O,"url('"+h.attr(E)+"')"),t[a.effect](a.effectTime),s&&(t.removeAttr(u+" "+l+" "+c+" "+a.imageBaseAttribute),f!==C&&t.removeAttr(f)),t.data(d,!0),y(T,t),h.remove(),p()});var m=(L&&A?A:t.attr(u))||"";h.attr(C,t.attr(f)).attr(F,t.attr(l)).attr(E,m?r+m:null),h.complete&&h.trigger(D)}}function A(t){var e=t.getBoundingClientRect(),r=a.scrollDirection,n=a.threshold,i=h()+n>e.top&&-n<e.bottom,o=g()+n>e.left&&-n<e.right;return"vertical"===r?i:"horizontal"===r?o:i&&o}function g(){return w>=0?w:w=n(t).width()}function h(){return B>=0?B:B=n(t).height()}function m(t){return t.tagName.toLowerCase()}function b(t,e){if(e){var r=t.split(",");t="";for(var a=0,n=r.length;a<n;a++)t+=e+r[a].trim()+(a!==n-1?",":"")}return t}function v(t,e){var n,i=0;return function(o,u){function l(){i=+new Date,e.call(r,o)}var f=+new Date-i;n&&clearTimeout(n),f>t||!a.enableThrottle||u?l():n=setTimeout(l,t-f)}}function p(){--z,i.length||z||y("onFinishedAll")}function y(t,e,n){return!!(t=a[t])&&(t.apply(r,[].slice.call(arguments,1)),!0)}var z=0,w=-1,B=-1,L=!1,T="afterLoad",D="load",I="error",N="img",E="src",F="srcset",C="sizes",O="background-image";"event"===a.bind||o?f():n(t).on(D+"."+l,f)}function a(a,o){var u=this,l=n.extend({},u.config,o),f={},c=l.name+"-"+ ++i;return u.config=function(t,r){return r===e?l[t]:(l[t]=r,u)},u.addItems=function(t){return f.a&&f.a("string"===n.type(t)?n(t):t),u},u.getItems=function(){return f.g?f.g():{}},u.update=function(t){return f.e&&f.e({},!t),u},u.force=function(t){return f.f&&f.f("string"===n.type(t)?n(t):t),u},u.loadAll=function(){return f.e&&f.e({all:!0},!0),u},u.destroy=function(){return n(l.appendScroll).off("."+c,f.e),n(t).off("."+c),f={},e},r(u,l,a,f,c),l.chainable?a:u}var n=t.jQuery||t.Zepto,i=0,o=!1;n.fn.Lazy=n.fn.lazy=function(t){return new a(this,t)},n.Lazy=n.lazy=function(t,r,i){if(n.isFunction(r)&&(i=r,r=[]),n.isFunction(i)){t=n.isArray(t)?t:[t],r=n.isArray(r)?r:[r];for(var o=a.prototype.config,u=o._f||(o._f={}),l=0,f=t.length;l<f;l++)(o[t[l]]===e||n.isFunction(o[t[l]]))&&(o[t[l]]=i);for(var c=0,s=r.length;c<s;c++)u[r[c]]=t[0]}},a.prototype.config={name:"lazy",chainable:!0,autoDestroy:!0,bind:"load",threshold:500,visibleOnly:!1,appendScroll:t,scrollDirection:"both",imageBase:null,defaultImage:"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",placeholder:null,delay:-1,combined:!1,attribute:"data-src",srcsetAttribute:"data-srcset",sizesAttribute:"data-sizes",retinaAttribute:"data-retina",loaderAttribute:"data-loader",imageBaseAttribute:"data-imagebase",removeAttribute:!0,handledName:"handled",loadedName:"loaded",effect:"show",effectTime:0,enableThrottle:!0,throttle:250,beforeLoad:e,afterLoad:e,onError:e,onFinishedAll:e},n(t).on("load",function(){o=!0})}(window);

  }).apply(root, arguments);
});
}(this));

define('WeltPixel_DesignElements/js/collapsible-mixin',['jquery'], function($) {
    'use strict';
    return function(collpsibleyWidget) {
        $.widget('mage.collapsible', $.mage.collapsible, {
            _scrollToTopIfVisible: function (elem) {
                var shouldScroll = window.Pearl && window.Pearl.scrollCollapsibleToTop;
                if (shouldScroll && !this._isElementOutOfViewport(elem)) {
                    elem.scrollIntoView();
                    window.scrollBy(0, -jQuery('.sticky-header').height() );
                }
            },
            _scrollToTopIfNotVisible: function () {
                var shouldScroll = window.Pearl && window.Pearl.scrollCollapsibleToTop;
                if (shouldScroll && this._isElementOutOfViewport()) {
                    this.header[0].scrollIntoView();
                    window.scrollBy(0, -jQuery('.sticky-header').height() );
                }
            },
        });
        return $.mage.collapsible;
    }
});

define('Born_Eds/js/edsValidator',[
    'jquery',
    'mage/validation'
], function ($) {
    "use strict";

    return function (validationMixin) {
        $.validator.addMethod(
            'edsvalidator',
            function (value) {
                if (value) {
                    return true;
                }
            },
            $.mage.__('EDS Code must be filled.')
        );
        return validationMixin;
    }
});

define('WeltPixel_CustomHeader/js/sticky_header_js',['jquery', 'Magento_Customer/js/customer-data', 'domReady!'], function ($, customerData) {
    stickyHeader = {
        stickyHeader: function () {
            var config = {
                pageWrapper:        $('.page-wrapper'),
                headerSection:      $('.page-wrapper div.page-header'),
                headerContent:      $('.header.content'),
                headerLogo:         $('.header.content').find('.logo'),
                panelWrapper:       $('.panel.wrapper'),
                navSection:         $('.sections.nav-sections'),
                searchBlock:        $('.header.content').find('.block-search').not('.wpx-block-search'),
                headerMultiStore:   $('.header-multistore'),
                switcherMultiStore: $('.multistore-switcher'),
                globalPromo:        $('.page-wrapper .page-header').find('.header-global-promo'),
                switcherCurrency:   $('.panel.wrapper').find('.switcher-currency'),
                greetWelcome:       $('.panel.wrapper').find('.greet.welcome'),
                headerPlaceholder:  '<div class="header-placeholder"></div>',
                stickyMobile:       window.stickyMobileEnabled,
                design:             $('.section-items .section-item-title').first().css('display') == 'none' ? 'desktop' : 'mobile',
                headerElHeight:     0,
                triggerEvent:       'scroll',
                stickyHeaderScrollUp : window.stickyHeaderScrollUpEnabled
            };

            /** abort if header-content or nav-sections was not found */
            if (config.headerContent.length == 0 || config.navSection.length == 0) {
                return;
            }

            var that = this;

            /** insert header-placeholder and move header elements */
            config.pageWrapper.prepend(config.headerPlaceholder);
            config.headerPlaceholder = $('.header-placeholder');

            if (that.getHeaderVersion(config.headerSection) != 'v3') {
                that.appendElements(config.headerSection, config.navSection, that, config);
            } else {
                that.appendElements(config.headerSection, null, that, config);
                config.headerContent.find('.compare.wrapper').after(config.navSection);
            }

            /** adjust header-placeholder height if global-promo-message is active */
            config.headerElHeight = parseInt(config.headerPlaceholder.outerHeight());
            var globalNotificationWrapper = config.globalPromo.find('.global-notification-wrapper');
            var checkHeight = setInterval(function () {
                if (globalNotificationWrapper.is(':visible') && globalNotificationWrapper.height()) {
                    if (config.headerPlaceholder.length) {
                        that.adjustHeaderPlaceholderHeight(that, config);
                        /** reset min-height on global message close */
                        globalNotificationWrapper.on('click', '.close-global-notification', function() {
                            that.adjustHeaderPlaceholderHeight(that, config);
                        });
                        clearInterval(checkHeight);
                    }
                }
            }, 500);

            /**
             * adjust the position of top-navigation
             * if its width is greater than the available room left in header-content
             */
            if (config.design != 'mobile') {
                that.adjustNavigation(that, config);
                that.fixFullWidthMenus(that, config);
            }

            $(window).on('scroll resize', function (e) {
                /** if design has changed force reset settings */
                if ($('body').hasClass('checkout-cart-index') && (e.type == 'resize')) {
                    return;
                }
                config.triggerEvent = e.type;
                var oldDesign = config.design;
                config.design = $('.section-items .section-item-title').first().css('display') == 'none' ? 'desktop' : 'mobile';
                if (oldDesign != config.design) {
                    that.resetSettings(that, config, oldDesign);
                }

                if (config.design == 'desktop') {
                    config.headerSection.removeClass('sticky-header-mobile');
                    switch (that.getHeaderVersion(config.headerSection))
                    {
                        case 'v1':
                            if (that.doSticky(that, config)) {
                                if (that.notStickyYet(config)) {
                                    that.moveElementsOnSticky(config.headerSection, config.navSection, 'out', config);
                                    config.searchBlock.after(config.navSection);
                                    that.showHideElements('hide', [
                                        config.switcherMultiStore,
                                        config.panelWrapper,
                                        config.headerMultiStore
                                    ]);
                                }
                            } else {
                                that.moveElementsOnSticky(config.headerSection, config.navSection, 'in', config);
                                config.headerSection.after(config.navSection);
                                that.showHideElements('show', [
                                    config.globalPromo,
                                    config.switcherMultiStore,
                                    config.panelWrapper,
                                    config.headerMultiStore
                                ]);
                            }
                            break;
                        case 'v2':
                            if (that.doSticky(that, config)) {
                                if (that.notStickyYet(config)) {
                                    that.moveElementsOnSticky(config.headerSection, config.navSection, 'out', config);
                                    if (!config.searchBlock.hasClass('minisearch-v2')) {
                                        config.searchBlock.after(config.navSection);
                                    } else {
                                        config.headerContent.find('.header_right').after(config.navSection);
                                    }
                                    that.showHideElements('hide', [
                                        config.switcherMultiStore,
                                        config.headerMultiStore
                                    ]);
                                }
                            } else {
                                that.moveElementsOnSticky(config.headerSection, config.navSection, 'in', config);
                                config.headerSection.after(config.navSection);
                                that.showHideElements('show', [
                                    config.globalPromo,
                                    config.switcherMultiStore,
                                    config.headerMultiStore
                                ]);
                            }
                            break;
                        case 'v3':
                            if (that.doSticky(that, config)) {
                                if (that.notStickyYet(config)) {
                                    that.moveElementsOnSticky(config.headerSection, null, 'out', config);
                                    that.showHideElements('hide', [
                                        config.switcherMultiStore,
                                        config.panelWrapper,
                                        config.headerMultiStore
                                    ]);
                                }
                            } else {
                                that.moveElementsOnSticky(config.headerSection, null, 'in', config);
                                that.showHideElements('show', [
                                    config.globalPromo,
                                    config.switcherMultiStore,
                                    config.panelWrapper,
                                    config.headerMultiStore
                                ]);
                            }
                            break;
                        case 'v4':
                            if (that.doSticky(that, config)) {
                                if (that.notStickyYet(config)) {
                                    that.moveElementsOnSticky(config.headerSection, config.navSection, 'out', config);
                                    config.navSection.addClass('sticky-header');
                                    that.showHideElements('hide', [
                                        config.switcherMultiStore,
                                        config.headerMultiStore
                                    ]);
                                    config.greetWelcome.css('visibility', 'hidden');
                                }
                            } else {
                                that.moveElementsOnSticky(config.headerSection, config.navSection, 'in', config);
                                config.navSection.removeClass('sticky-header');
                                that.showHideElements('show', [
                                    config.globalPromo,
                                    config.switcherMultiStore,
                                    config.headerMultiStore
                                ]);
                                config.greetWelcome.css('visibility', 'visible');
                            }
                            break;
                        default:
                            // nothing to do here
                            break;
                    }

                    /**
                     * adjust the position of top-navigation and the width of full-width sub-menu
                     */
                    if (config.design != 'mobile') {
                        that.adjustNavigation(that, config);
                        that.fixFullWidthMenus(that, config);
                    }


                } else {
                    config.headerSection.removeClass('sticky-header');
                    config.navSection.removeClass('sticky-header sticky-header-nav');

                    if (that.getHeaderVersion(config.headerSection) != 'v3') {
                        config.headerSection.after(config.navSection);
                    } else {
                        config.navSection.appendTo(config.headerContent);
                        if (that.getHeaderVersion(config.headerSection) != 'v2') {
                            config.switcherMultiStore.hide();
                        }
                    }

                    if (config.stickyMobile == 1) {
                        var headerVersion = that.getHeaderVersion(config.headerSection);
                        if (that.doSticky(that, config)) {
                            config.headerSection.addClass('sticky-header-mobile');
                            if (headerVersion != 'v2' && headerVersion != 'v4') {
                                that.showHideElements('hide', [
                                    config.panelWrapper
                                ]);
                            }

                            that.showHideElements('hide', [
                                config.globalPromo,
                                config.headerMultiStore
                            ]);
                        } else {
                            config.headerSection.removeClass('sticky-header-mobile');
                            if (headerVersion != 'v2' && headerVersion != 'v4') {
                                that.showHideElements('show', [
                                    config.panelWrapper
                                ]);
                            }

                            that.showHideElements('show', [
                                config.globalPromo,
                                config.headerMultiStore
                            ]);
                        }
                    }
                }
            });
        },
        adjustHeaderPlaceholderHeight: function (that, config, oldDesign) {
            if (oldDesign == 'mobile') {
                setTimeout(function() {
                    config.headerPlaceholder.css('min-height', '');
                    config.headerPlaceholder.css('min-height', parseInt(config.headerPlaceholder.outerHeight()) + 'px');
                }, 250);
            } else {
                config.headerPlaceholder.css('min-height', '');
                config.headerPlaceholder.css('min-height', parseInt(config.headerPlaceholder.outerHeight()) + 'px');
            }
        },
        resetSettings: function (that, config, oldDesign) {
            config.headerElHeight = 0;
            if (that.getHeaderVersion(config.headerSection) != 'v3') {
                that.appendElements(config.headerSection, config.navSection, that, config, oldDesign);
            } else {
                that.appendElements(config.headerSection, null, that, config, oldDesign);
                config.headerContent.find('.compare.wrapper').after(config.navSection);
            }
        },
        appendElements: function (a, b, that, config, oldDesign) {
            if (a) {
                a.appendTo(config.headerPlaceholder);
            }

            if (b) {
                b.appendTo(config.headerPlaceholder);
            }
            that.adjustHeaderPlaceholderHeight(that, config, oldDesign);
        },
        notStickyYet: function (config) {
            return !config.headerSection.hasClass('sticky-header');
        },
        doSticky: function (that, config) {

            if (config.stickyHeaderScrollUp === '1') {
                let position = $(window).scrollTop(),
                    sticky = false;

                if (position < this.lastScrollPosition && position !== 0) {
                    sticky = true;
                }
                this.lastScrollPosition = position;
                return sticky;
            } else {
                let position = $(window).scrollTop(),
                    sticky = false;
                if (position !== 0) {
                    sticky = true;
                }
                this.lastScrollPosition = position;
                return sticky;
            }
        },
        moveElementsOnSticky: function (a, b, direction, config) {
            if (direction == 'out') {
                if (b) {
                    b.prependTo($('.page-wrapper')).before(config.headerPlaceholder);
                    b.addClass('sticky-header-nav');
                }
                if (a) {
                    a.prependTo($('.page-wrapper')).before(config.headerPlaceholder);
                    a.addClass('sticky-header');
                }
            } else {
                if (a) {
                    a.appendTo(config.headerPlaceholder);
                    a.removeClass('sticky-header');
                }
                if (b) {
                    b.appendTo(config.headerPlaceholder);
                    b.removeClass('sticky-header-nav');
                }
            }
            if (config.triggerEvent == "scroll" && $('.minicart-wrapper .actions .paypal-logo').length > 0) {
                $('.minicart-wrapper .extra-actions').css('height', $('.minicart-wrapper .extra-actions').height() + 'px');
                $('.minicart-wrapper .actions .paypal-logo').hide();
                customerData.reload(['cart'], false).done(function () {
                    $('.minicart-wrapper .actions .paypal-logo').show();
                });
            }
        },
        showHideElements: function (action, els) {
            for (var i = 0; i < els.length; i++) {
                if (action == 'show') {
                    els[i].slideDown('fast');
                } else {
                    els[i].hide();
                }
            }
        },
        getHeaderVersion: function (headerSection) {
            if (headerSection.hasClass('page-header-v1')) {
                return 'v1';
            } else if (headerSection.hasClass('page-header-v2')) {
                return 'v2';
            } else if (headerSection.hasClass('page-header-v3')) {
                return 'v3';
            } else if (headerSection.hasClass('page-header-v4')) {
                return 'v4';
            }
        },
        adjustNavigation: function (that, config) {
            var navigationLis    = config.navSection.find('.navigation li.level0'),
                headerW          = config.headerContent.outerWidth(),
                logoW            = config.headerLogo.outerWidth(),
                headerMinicartW  = config.headerContent.find('.minicart-wrapper').outerWidth(),
                fullwidthWrapper = config.navSection.find('.fullwidth-wrapper'),
                searchBlockW    = 0;

            if (that.getHeaderVersion(config.headerSection) != 'v4') {
                /** get the real width of active search-block */
                config.searchBlock.each(function() {
                    if (!$(this).hasClass('wpx-block-search')) {
                        searchBlockW = $(this).outerWidth();
                    }
                });

                /** get the real width of the top-navigation container */
                var navigationW = 0, navCount = 0;
                navigationLis.each(function() {
                    navCount++;
                    navigationW += $(this).outerWidth();
                    if (navCount < navigationLis.length)
                        navigationW += 10; // left margin of nav li
                });

                /** do some math */
                var navRoom = headerW - logoW - headerMinicartW - searchBlockW - 80;
                var headerLinks = config.headerContent.find('.header.content .header.links');
                if (headerLinks.length && headerLinks.is(':visible')) {
                    navRoom -= config.headerContent.find('.header.content .header.links').outerWidth();
                }

                /** apply or remove adjustments */
                if (navigationW >= navRoom) {
                    config.navSection.addClass('too-wide');
                    config.headerContent.css('padding-bottom', '10px');
                    fullwidthWrapper.find('.columns-group').first().css({'margin-left': 'initial'});
                } else {
                    config.navSection.removeClass('too-wide');
                    config.headerContent.css('padding-bottom', '');
                    fullwidthWrapper.find('.columns-group').first().css({'margin-left': '-20px'});
                }
            }
            /** fix top position of sub-menus for header-v3 */
            if (that.getHeaderVersion(config.headerSection) == 'v3') {
                config.navSection.find('.level0.submenu').each(function() {
                    $(this).addClass('top-moved');
                });
            }
        },
        fixFullWidthMenus: function (that, config) {
            var pageWrapperW = config.pageWrapper.width(),
                headerContentW = config.headerContent.outerWidth(),
                leftPosition = parseInt(((pageWrapperW - headerContentW) / 2) * -1),
                navSectionLeft = parseInt(config.navSection.offset().left * -1),
                fullwidthWrapper = config.navSection.find('.fullwidth-wrapper'),
                headerVersion = stickyHeader.getHeaderVersion(config.headerSection);

            switch (headerVersion) {
                case 'v3':
                    fullwidthWrapper.css({'left': leftPosition + 'px'});
                    break;
                default:
                    if (that.notStickyYet(config)) {
                        fullwidthWrapper.css({'left': ''});
                    } else {
                        fullwidthWrapper.css({'left': navSectionLeft + 'px'});
                    }
                    break;
            }


        },
        lastScrollPosition: 0
    };

    return stickyHeader;
});

define('WeltPixel_DesignElements/js/designelements_base',['jquery', 'jRespond'], function ($) {
    "use strict";

    var $body = $('body');
    var SEMICOLONBASE = SEMICOLONBASE || {};

    SEMICOLONBASE.widget = {
        init: function (options) {
            this.breakpoints = options.breakpoints;
            SEMICOLONBASE.widget.responsiveWpClasses();
        },
        responsiveWpClasses: function(){
            var jRes = jRespond([
                    {
                            label: 'xxs',
                            enter: this.breakpoints.xxs.enter,
                            exit: this.breakpoints.xxs.exit
                    },{
                            label: 'xs',
                            enter: this.breakpoints.xs.enter,
                            exit: this.breakpoints.xs.exit
                    },{
                            label: 's',
                            enter: this.breakpoints.s.enter,
                            exit: this.breakpoints.s.exit
                    },{
                            label: 'm',
                            enter: this.breakpoints.m.enter,
                            exit: this.breakpoints.m.exit
                    },{
                            label: 'l',
                            enter: this.breakpoints.l.enter,
                            exit: this.breakpoints.l.exit
                    },{
                            label: 'xl',
                            enter: this.breakpoints.xl.enter,
                            exit: this.breakpoints.xl.exit
                    }
            ]);
            jRes.addFunc([
                    {
                            breakpoint: 'xxs',
                            enter: function() { $body.addClass('wp-device-xxs'); },
                            exit: function() { $body.removeClass('wp-device-xxs'); }
                    },{
                            breakpoint: 'xs',
                            enter: function() { $body.addClass('wp-device-xs'); },
                            exit: function() { $body.removeClass('wp-device-xs'); }
                    },{
                            breakpoint: 's',
                            enter: function() { $body.addClass('wp-device-s'); },
                            exit: function() { $body.removeClass('wp-device-s'); }
                    },{
                            breakpoint: 'm',
                            enter: function() { $body.addClass('wp-device-m'); },
                            exit: function() { $body.removeClass('wp-device-m'); }
                    },{
                            breakpoint: 'l',
                            enter: function() { $body.addClass('wp-device-l'); },
                            exit: function() { $body.removeClass('wp-device-l'); }
                    },{
                            breakpoint: 'xl',
                            enter: function() { $body.addClass('wp-device-xl'); },
                            exit: function() { $body.removeClass('wp-device-xl'); }
                    }
            ]);
        }
    };

    return SEMICOLONBASE;
});


define("bundles/common", function(){});

//# sourceMappingURL=common.js.map