PageSourceSearch

https://webgood.co.kr/ind-script/optimizer.php?filename=rZhbc-o2EM…e1e78de7f6e423f97d86d843ade4cb&t=1790658558

js webgood.co.kr collected 2026-10-06 13:59:25 UTC 1,435,978 bytes, 33,682 lines download raw bytes

1(function (root, factory) {
2    //amd
3    if (typeof define === "function" && define.amd) {
4        define(['sprintf-js'], function (sprintf) {
5            return factory(sprintf.vsprintf);
6        });
7
8        //commonjs
9    } else if (typeof module === "object" && module.exports) {
10        module.exports = factory(require('sprintf-js').vsprintf);
11
12        //global
13    } else {
14        root.I18nTranslator = factory(window.vsprintf);
15    }
16
17    var i18n = new I18nTranslator(TRANSLATIONS);
18    window['__'] = function (sMsg, sGroupID) {
19        return i18n.p__(sGroupID, sMsg);
20    };
21
22    window['__pn'] = function (sMsgID, sGroupID, iValue) {
23        if (iValue === undefined || I18N_FN.isNumber(iValue) === false) {
24            iValue = 0;
25        }
26        return i18n.np__(sGroupID, sMsgID, sMsgID + '.PLURAL', iValue);
27    };
28
29    window['__sl'] = function (languageCode) {
30        if (!languageCode) {
31            console.error('Language code is required for __sl');
32            return false;
33        }
34        
35        // 언어코드 형식 검증 (en_US 같은 5자리 형식)
36        if (!/^[a-z]{2}_[A-Z]{2}$/.test(languageCode)) {
37            console.error('Invalid language code format. Expected format: en_US (5 characters)');
38            return false;
39        }
40
41        // G2G 환경 체크
42        if (!window.CAFE24?.SHOP?.isG2G?.() && !window.CAFE24?.SHOP?.isGlobale?.()) {
43            console.error('Language switching is only available in G2G environment');
44            return false;
45        }        
46        
47        try {
48            // 현재 시간을 버전으로 사용
49            var currentTime = new Date().getTime();
50            var apiUrl = '/ind-script/i18n.php?v=' + currentTime + '&lang=' + languageCode + '&domain=front&type=G2G&format=json';
51            
52            fetch(apiUrl)
53                .then(function(response) {
54                    if (!response.ok) {
55                        throw new Error('HTTP error! status: ' + response.status);
56                    }
57                    return response.json();
58                })
59                .then(function(translationData) {
60                    // 응답 데이터 검증 (필수 키들 체크)
61                    if (!translationData || 
62                        !translationData.domain || 
63                        !translationData['plural-forms'] || 
64                        !translationData.messages
65                    ) {
66                        console.error('Invalid translation data format');
67                        return;
68                    }
69                    
70                    // 기존 번역 데이터 초기화
71                    i18n.dictionary = {};
72                    i18n.plurals = {};
73                    i18n.domain = null;
74                    
75                    // 새로운 번역 데이터 로드
76                    i18n.loadTranslations(translationData);
77                    
78                    console.log('Language switched to:', languageCode);
79                })
80                .catch(function(error) {
81                    console.error('Failed to load language:', languageCode, error);
82                });
83        } catch (error) {
84            console.error('Error in __sl function:', error);
85            return false;
86        }
87    };
88}(this, function (vsprintf) {
89    "use strict";
90    function I18nTranslator (translations) {
91        this.dictionary = {};
92        this.plurals = {};
93        this.domain = null;
94
95        if (translations) {
96            this.loadTranslations(translations);
97        }
98    }
99
100    I18nTranslator.prototype = {
101        loadTranslations: function (translations) {
102            var domain = translations.domain || '';
103
104            if (this.domain === null) {
105                this.domain = domain;
106            }
107
108            if (this.dictionary[domain]) {
109                mergeTranslations(this.dictionary[domain], translations.messages);
110                return this;
111            }
112
113            if (translations.fn) {
114                this.plurals[domain] = { fn: translations.fn };
115            } else if (translations['plural-forms']) {
116                var plural = translations['plural-forms'].split(';', 2);
117
118                this.plurals[domain] = {
119                    count: parseInt(plural[0].replace('nplurals=', '')),
120                    code: plural[1].replace('plural=', 'return ') + ';'
121                };
122            }
123
124            this.dictionary[domain] = translations.messages;
125
126            return this;
127        },
128
129        defaultDomain: function (domain) {
130            this.domain = domain;
131
132            return this;
133        },
134
135        gettext: function (original) {
136            return this.dpgettext(this.domain, null, original);
137        },
138
139        ngettext: function (original, plural, value) {
140            return this.dnpgettext(this.domain, null, original, plural, value);
141        },
142
143        dngettext: function (domain, original, plural, value) {
144            return this.dnpgettext(domain, null, original, plural, value);
145        },
146
147        npgettext: function (context, original, plural, value) {
148            return this.dnpgettext(this.domain, context, original, plural, value);
149        },
150
151        pgettext: function (context, original) {
152            return this.dpgettext(this.domain, context, original);
153        },
154
155        dgettext: function (domain, original) {
156            return this.dpgettext(domain, null, original);
157        },
158
159        dpgettext: function (domain, context, original) {
160            var translation = getTranslation(this.dictionary, domain, context, original);
161
162            if (translation !== false && translation[0] !== '') {
163                return translation[0];
164            }
165
166            return original;
167        },
168
169        dnpgettext: function (domain, context, original, plural, value) {
170            var index = getPluralIndex(this.plurals, domain, value);
171            var translation = getTranslation(this.dictionary, domain, context, original);
172
173            if (translation[index] && translation[index] !== '') {
174                return translation[index];
175            }
176
177            return (index === 0) ? original : plural;
178        },
179
180        __: function (original) {
181            return format(
182                this.gettext(original),
183                Array.prototype.slice.call(arguments, 1)
184            );
185        },
186
187        n__: function (original, plural, value) {
188            return format(
189                this.ngettext(original, plural, value),
190                Array.prototype.slice.call(arguments, 3)
191            );
192        },
193
194        p__: function (context, original) {
195            return format(
196                this.pgettext(context, original),
197                Array.prototype.slice.call(arguments, 2)
198            );
199        },
200
201        d__: function (domain, original) {
202            return format(
203                this.dgettext(domain, original),
204                Array.prototype.slice.call(arguments, 2)
205            );
206        },
207
208        dp__: function (domain, context, original) {
209            return format(
210                this.dgettext(domain, context, original),
211                Array.prototype.slice.call(arguments, 3)
212            );
213        },
214
215        np__: function (context, original, plural, value) {
216            return format(
217                this.npgettext(context, original, plural, value),
218                Array.prototype.slice.call(arguments, 4)
219            );
220        },
221
222        dnp__: function (domain, context, original, plural, value) {
223            return format(
224                this.dnpgettext(domain, context, original, plural, value),
225                Array.prototype.slice.call(arguments, 5)
226            );
227        }
228    };
229
230    function getTranslation(dictionary, domain, context, original) {
231        context = context || '';
232
233        if (!dictionary[domain] || !dictionary[domain][context] || !dictionary[domain][context][original]) {
234            return false;
235        }
236
237        try {
238            I18N_LOG_COLLECT.set(original, context);
239        } catch (e) {}
240
241        return dictionary[domain][context][original];
242    }
243
244    function getPluralIndex(plurals, domain, value) {
245        if (!plurals[domain]) {
246            return value == 1 ? 0 : 1;
247        }
248
249        if (!plurals[domain].fn) {
250            plurals[domain].fn = new Function('n', plurals[domain].code);
251        }
252
253        return plurals[domain].fn.call(this, value) + 0;
254    }
255
256    function mergeTranslations(translations, newTranslations) {
257        for (var context in newTranslations) {
258            if (!translations[context]) {
259                translations[context] = newTranslations[context];
260                continue;
261            }
262
263            for (var original in newTranslations[context]) {
264                translations[context][original] = newTranslations[context][original];
265            }
266        }
267    }
268
269    function format (text, args) {
270        if (!args.length) {
271            return text;
272        }
273
274        if (args[0] instanceof Array) {
275            return vsprintf(text, args[0]);
276        }
277
278        return vsprintf(text, args);
279    }
280
281    return I18nTranslator;
282}));
283
284/**
285 * i18n 관련 함수 모음
286 * @type {{ordinalSuffixes: string[], ordinalNumber: I18N_FN.ordinalNumber}}
287 */
288var I18N_FN = {
289    ordinalSuffixes: ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'],
290
291    ordinalNumber: function (iValue) {
292        if (iValue === undefined) {
293            return '';
294        }
295
296        var iNum = String(iValue).replace(/,/g, "");
297        if (this.isNumber(iNum) === false) {
298            return iValue;
299        }
300        if (__('__LANGUAGE.CODE__') !== 'en_US') {
301            return iValue;
302        }
303        iNum = Math.abs(iNum);
304        iNum = parseFloat(iNum);
305        if (((iNum % 100) >= 11 && ((iNum % 100) <= 13)) || iNum % 1 != 0) {
306            return iValue + 'th';
307        }
308
309        return iValue + this.ordinalSuffixes[iNum % 10];
310    },
311    isNumber: function (v) {
312        return /^[+-]?\d*(\.?\d*)$/.test(v);
313    }
314};
315
316var I18N_LOG_COLLECT = {
317    aTranslationCodes: [],
318    bIsCallApiOnLoaded: false,
319    request_url: window.location.pathname,
320
321    call: function () {
322        var data = I18N_LOG_COLLECT.aTranslationCodes;
323        if (data.length === 0) {
324            return false;
325        }
326        I18N_LOG_COLLECT.aTranslationCodes = [];
327        $.ajax({
328            url: '/exec/common/translate/logging',
329            data: {"data": data},
330            type: 'POST',
331            dataType: 'json',
332            success: function (aData) {}
333        });
334    },
335    set: function (sMsg_id, sGroup_id) {
336        if (typeof CAFE24.TRANSLATE_LOG_STATUS === 'undefined' || CAFE24.TRANSLATE_LOG_STATUS !== 'T') {
337            return;
338        }
339
340        var item = {
341            'request_url': I18N_LOG_COLLECT.request_url,
342            'msg_id': sMsg_id,
343            'group_id': sGroup_id
344        };
345
346        if (I18N_LOG_COLLECT.bIsCallApiOnLoaded) {
347            I18N_LOG_COLLECT.aTranslationCodes.push(item);
348            I18N_LOG_COLLECT.call();
349            return true;
350        }
351        I18N_LOG_COLLECT.aTranslationCodes.push(item);
352    },
353    loadComplete: function () {
354        I18N_LOG_COLLECT.bIsCallApiOnLoaded = true;
355        I18N_LOG_COLLECT.call();
356    }
357};
358
359if (typeof CAFE24.TRANSLATE_LOG_STATUS !== 'undefined' && CAFE24.TRANSLATE_LOG_STATUS === 'T') {
360    if (document.addEventListener) {
361        document.addEventListener("DOMContentLoaded", function () {
362            I18N_LOG_COLLECT.loadComplete();
363        }, false);
364    } else if (document.attachEvent) {
365        document.attachEvent("onreadystatechange", function () {
366            if (document.readyState === "complete") {
367                document.detachEvent("onreadystatechange", arguments.callee);
368                I18N_LOG_COLLECT.loadComplete();
369            }
370        });
371    }
372}
373/*!
374 * jQuery JavaScript Library v3.6.0
375 * https://jquery.com/
376 *
377 * Includes Sizzle.js
378 * https://sizzlejs.com/
379 *
380 * Copyright OpenJS Foundation and other contributors
381 * Released under the MIT license
382 * https://jquery.org/license
383 *
384 * Date: 2021-03-02T17:08Z
385 */
386( function( global, factory ) {
387
388    "use strict";
389
390    if ( typeof module === "object" && typeof module.exports === "object" ) {
391
392        // For CommonJS and CommonJS-like environments where a proper `window`
393        // is present, execute the factory and get jQuery.
394        // For environments that do not have a `window` with a `document`
395        // (such as Node.js), expose a factory as module.exports.
396        // This accentuates the need for the creation of a real `window`.
397        // e.g. var jQuery = require("jquery")(window);
398        // See ticket #14549 for more info.
399        module.exports = global.document ?
400            factory( global, true ) :
401            function( w ) {
402                if ( !w.document ) {
403                    throw new Error( "jQuery requires a window with a document" );
404                }
405                return factory( w );
406            };
407    } else {
408        factory( global );
409    }
410
411// Pass this if window is not defined yet
412} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
413
414// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
415// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
416// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), str
vendor: 4,813 bytes, lines 416-570
416ict mode should be common
417// enough that all such attempts are guarded in a try block.
418    "use strict";
419
420    var arr = [];
421
422    var getProto = Object.getPrototypeOf;
423
424    var slice = arr.slice;
425
426    var flat = arr.flat ? function( array ) {
427        return arr.flat.call( array );
428    } : function( array ) {
429        return arr.concat.apply( [], array );
430    };
431
432
433    var push = arr.push;
434
435    var indexOf = arr.indexOf;
436
437    var class2type = {};
438
439    var toString = class2type.toString;
440
441    var hasOwn = class2type.hasOwnProperty;
442
443    var fnToString = hasOwn.toString;
444
445    var ObjectFunctionString = fnToString.call( Object );
446
447    var support = {};
448
449    var isFunction = function isFunction( obj ) {
450
451        // Support: Chrome <=57, Firefox <=52
452        // In some browsers, typeof returns "function" for HTML <object> elements
453        // (i.e., `typeof document.createElement( "object" ) === "function"`).
454        // We don't want to classify *any* DOM node as a function.
455        // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
456        // Plus for old WebKit, typeof returns "function" for HTML collections
457        // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
458        return typeof obj === "function" && typeof obj.nodeType !== "number" &&
459            typeof obj.item !== "function";
460    };
461
462
463    var isWindow = function isWindow( obj ) {
464        return obj != null && obj === obj.window;
465    };
466
467
468    var document = window.document;
469
470
471
472    var preservedScriptAttributes = {
473        type: true,
474        src: true,
475        nonce: true,
476        noModule: true
477    };
478
479    function DOMEval( code, node, doc ) {
480        doc = doc || document;
481
482        var i, val,
483            script = doc.createElement( "script" );
484
485        script.text = code;
486        if ( node ) {
487            for ( i in preservedScriptAttributes ) {
488
489                // Support: Firefox 64+, Edge 18+
490                // Some browsers don't support the "nonce" property on scripts.
491                // On the other hand, just using `getAttribute` is not enough as
492                // the `nonce` attribute is reset to an empty string whenever it
493                // becomes browsing-context connected.
494                // See https://github.com/whatwg/html/issues/2369
495                // See https://html.spec.whatwg.org/#nonce-attributes
496                // The `node.getAttribute` check was added for the sake of
497                // `jQuery.globalEval` so that it can fake a nonce-containing node
498                // via an object.
499                val = node[ i ] || node.getAttribute && node.getAttribute( i );
500                if ( val ) {
501                    script.setAttribute( i, val );
502                }
503            }
504        }
505        doc.head.appendChild( script ).parentNode.removeChild( script );
506    }
507
508
509    function toType( obj ) {
510        if ( obj == null ) {
511            return obj + "";
512        }
513
514        // Support: Android <=2.3 only (functionish RegExp)
515        return typeof obj === "object" || typeof obj === "function" ?
516            class2type[ toString.call( obj ) ] || "object" :
517            typeof obj;
518    }
519    /* global Symbol */
520// Defining this global in .eslintrc.json would create a danger of using the global
521// unguarded in another place, it seems safer to define global only for this module
522
523
524
525    var
526        version = "3.6.0",
527
528        // Define a local copy of jQuery
529        jQuery = function( selector, context ) {
530
531            // The jQuery object is actually just the init constructor 'enhanced'
532            // Need init if jQuery is called (just allow error to be thrown if not included)
533            return new jQuery.fn.init( selector, context );
534        };
535
536    jQuery.fn = jQuery.prototype = {
537
538        // The current version of jQuery being used
539        jquery: version,
540
541        constructor: jQuery,
542
543        // The default length of a jQuery object is 0
544        length: 0,
545
546        toArray: function() {
547            return slice.call( this );
548        },
549
550        // Get the Nth element in the matched element set OR
551        // Get the whole matched element set as a clean array
552        get: function( num ) {
553
554            // Return all the elements in a clean array
555            if ( num == null ) {
556                return slice.call( this );
557            }
558
559            // Return just the one element from the set
560            return num < 0 ? this[ num + this.length ] : this[ num ];
561        },
562
563        // Take an array of elements and push it onto the stack
564        // (returning the new matched element set)
565        pushStack: function( elems ) {
566
567            // Build a new jQuery matched element set
568            var ret = jQuery.merge( this.constructor(), elems );
569
570            // Add the old object onto the stack (as a reference)
vendor: 4,654 bytes, lines 571-721
571            ret.prevObject = this;
572
573            // Return the newly-formed element set
574            return ret;
575        },
576
577        // Execute a callback for every element in the matched set.
578        each: function( callback ) {
579            return jQuery.each( this, callback );
580        },
581
582        map: function( callback ) {
583            return this.pushStack( jQuery.map( this, function( elem, i ) {
584                return callback.call( elem, i, elem );
585            } ) );
586        },
587
588        slice: function() {
589            return this.pushStack( slice.apply( this, arguments ) );
590        },
591
592        first: function() {
593            return this.eq( 0 );
594        },
595
596        last: function() {
597            return this.eq( -1 );
598        },
599
600        even: function() {
601            return this.pushStack( jQuery.grep( this, function( _elem, i ) {
602                return ( i + 1 ) % 2;
603            } ) );
604        },
605
606        odd: function() {
607            return this.pushStack( jQuery.grep( this, function( _elem, i ) {
608                return i % 2;
609            } ) );
610        },
611
612        eq: function( i ) {
613            var len = this.length,
614                j = +i + ( i < 0 ? len : 0 );
615            return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
616        },
617
618        end: function() {
619            return this.prevObject || this.constructor();
620        },
621
622        // For internal use only.
623        // Behaves like an Array's method, not like a jQuery method.
624        push: push,
625        sort: arr.sort,
626        splice: arr.splice
627    };
628
629    jQuery.extend = jQuery.fn.extend = function() {
630        var options, name, src, copy, copyIsArray, clone,
631            target = arguments[ 0 ] || {},
632            i = 1,
633            length = arguments.length,
634            deep = false;
635
636        // Handle a deep copy situation
637        if ( typeof target === "boolean" ) {
638            deep = target;
639
640            // Skip the boolean and the target
641            target = arguments[ i ] || {};
642            i++;
643        }
644
645        // Handle case when target is a string or something (possible in deep copy)
646        if ( typeof target !== "object" && !isFunction( target ) ) {
647            target = {};
648        }
649
650        // Extend jQuery itself if only one argument is passed
651        if ( i === length ) {
652            target = this;
653            i--;
654        }
655
656        for ( ; i < length; i++ ) {
657
658            // Only deal with non-null/undefined values
659            if ( ( options = arguments[ i ] ) != null ) {
660
661                // Extend the base object
662                for ( name in options ) {
663                    copy = options[ name ];
664
665                    // Prevent Object.prototype pollution
666                    // Prevent never-ending loop
667                    if ( name === "__proto__" || target === copy ) {
668                        continue;
669                    }
670
671                    // Recurse if we're merging plain objects or arrays
672                    if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
673                        ( copyIsArray = Array.isArray( copy ) ) ) ) {
674                        src = target[ name ];
675
676                        // Ensure proper type for the source value
677                        if ( copyIsArray && !Array.isArray( src ) ) {
678                            clone = [];
679                        } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
680                            clone = {};
681                        } else {
682                            clone = src;
683                        }
684                        copyIsArray = false;
685
686                        // Never move original objects, clone them
687                        target[ name ] = jQuery.extend( deep, clone, copy );
688
689                        // Don't bring in undefined values
690                    } else if ( copy !== undefined ) {
691                        target[ name ] = copy;
692                    }
693                }
694            }
695        }
696
697        // Return the modified object
698        return target;
699    };
700
701    jQuery.extend( {
702
703        // Unique for each copy of jQuery on the page
704        expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
705
706        // Assume jQuery is ready without the ready module
707        isReady: true,
708
709        error: function( msg ) {
710            throw new Error( msg );
711        },
712
713        noop: function() {},
714
715        isPlainObject: function( obj ) {
716            var key;
717
718            // Detect obvious negatives
719            // Use toString instead of jQuery.type to catch host objects
720            if ( !obj || toString.call( obj ) !== "[object Object]" ) {
721                return false;
vendor: 4,823 bytes, lines 722-882
722            }
723
724            // Not own constructor property must be Object
725            if ( obj.constructor &&
726                !hasOwn.call( obj, "constructor" ) &&
727                !hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) {
728                return false;
729            }
730
731            // Own properties are enumerated firstly, so to speed up,
732            // if last one is own, then all properties are own
733            for ( key in obj ) {}
734
735            return key === undefined || hasOwn.call( obj, key );
736        },
737
738        isEmptyObject: function( obj ) {
739            var name;
740
741            for ( name in obj ) {
742                return false;
743            }
744            return true;
745        },
746
747        // Evaluates a script in a provided context; falls back to the global one
748        // if not specified.
749        globalEval: function( code, options, doc ) {
750            DOMEval( code, { nonce: options && options.nonce }, doc );
751        },
752
753        each: function( obj, callback ) {
754            var length, i = 0;
755
756            if ( isArrayLike( obj ) ) {
757                length = obj.length;
758                for ( ; i < length; i++ ) {
759                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
760                        break;
761                    }
762                }
763            } else {
764                for ( i in obj ) {
765                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
766                        break;
767                    }
768                }
769            }
770
771            return obj;
772        },
773
774        // results is for internal usage only
775        makeArray: function( arr, results ) {
776            var ret = results || [];
777
778            if ( arr != null ) {
779                if ( isArrayLike( Object( arr ) ) ) {
780                    jQuery.merge( ret,
781                        typeof arr === "string" ?
782                            [ arr ] : arr
783                    );
784                } else {
785                    push.call( ret, arr );
786                }
787            }
788
789            return ret;
790        },
791
792        inArray: function( elem, arr, i ) {
793            return arr == null ? -1 : indexOf.call( arr, elem, i );
794        },
795
796        // Support: Android <=4.0 only, PhantomJS 1 only
797        // push.apply(_, arraylike) throws on ancient WebKit
798        merge: function( first, second ) {
799            var len = +second.length,
800                j = 0,
801                i = first.length;
802
803            for ( ; j < len; j++ ) {
804                first[ i++ ] = second[ j ];
805            }
806
807            first.length = i;
808
809            return first;
810        },
811
812        grep: function( elems, callback, invert ) {
813            var callbackInverse,
814                matches = [],
815                i = 0,
816                length = elems.length,
817                callbackExpect = !invert;
818
819            // Go through the array, only saving the items
820            // that pass the validator function
821            for ( ; i < length; i++ ) {
822                callbackInverse = !callback( elems[ i ], i );
823                if ( callbackInverse !== callbackExpect ) {
824                    matches.push( elems[ i ] );
825                }
826            }
827
828            return matches;
829        },
830
831        // arg is for internal usage only
832        map: function( elems, callback, arg ) {
833            var length, value,
834                i = 0,
835                ret = [];
836
837            // Go through the array, translating each of the items to their new values
838            if ( isArrayLike( elems ) ) {
839                length = elems.length;
840                for ( ; i < length; i++ ) {
841                    value = callback( elems[ i ], i, arg );
842
843                    if ( value != null ) {
844                        ret.push( value );
845                    }
846                }
847
848                // Go through every key on the object,
849            } else {
850                for ( i in elems ) {
851                    value = callback( elems[ i ], i, arg );
852
853                    if ( value != null ) {
854                        ret.push( value );
855                    }
856                }
857            }
858
859            // Flatten any nested arrays
860            return flat( ret );
861        },
862
863        // A global GUID counter for objects
864        guid: 1,
865
866        // jQuery.support is not used in Core but other projects attach their
867        // properties to it so it needs to exist.
868        support: support
869    } );
870
871    if ( typeof Symbol === "function" ) {
872        jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
873    }
874
875// Populate the class2type map
876    jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
877        function( _i, name ) {
878            class2type[ "[object " + name + "]" ] = name.toLowerCase();
879        } );
880
881    function isArrayLike( obj ) {
882
vendor: 18,971 bytes, lines 883-1316
883        // Support: real iOS 8.2 only (not reproducible in simulator)
884        // `in` check used to prevent JIT error (gh-2145)
885        // hasOwn isn't used here due to false negatives
886        // regarding Nodelist length in IE
887        var length = !!obj && "length" in obj && obj.length,
888            type = toType( obj );
889
890        if ( isFunction( obj ) || isWindow( obj ) ) {
891            return false;
892        }
893
894        return type === "array" || length === 0 ||
895            typeof length === "number" && length > 0 && ( length - 1 ) in obj;
896    }
897    var Sizzle =
898        /*!
899 * Sizzle CSS Selector Engine v2.3.6
900 * https://sizzlejs.com/
901 *
902 * Copyright JS Foundation and other contributors
903 * Released under the MIT license
904 * https://js.foundation/
905 *
906 * Date: 2021-02-16
907 */
908        ( function( window ) {
909            var i,
910                support,
911                Expr,
912                getText,
913                isXML,
914                tokenize,
915                compile,
916                select,
917                outermostContext,
918                sortInput,
919                hasDuplicate,
920
921                // Local document vars
922                setDocument,
923                document,
924                docElem,
925                documentIsHTML,
926                rbuggyQSA,
927                rbuggyMatches,
928                matches,
929                contains,
930
931                // Instance-specific data
932                expando = "sizzle" + 1 * new Date(),
933                preferredDoc = window.document,
934                dirruns = 0,
935                done = 0,
936                classCache = createCache(),
937                tokenCache = createCache(),
938                compilerCache = createCache(),
939                nonnativeSelectorCache = createCache(),
940                sortOrder = function( a, b ) {
941                    if ( a === b ) {
942                        hasDuplicate = true;
943                    }
944                    return 0;
945                },
946
947                // Instance methods
948                hasOwn = ( {} ).hasOwnProperty,
949                arr = [],
950                pop = arr.pop,
951                pushNative = arr.push,
952                push = arr.push,
953                slice = arr.slice,
954
955                // Use a stripped-down indexOf as it's faster than native
956                // https://jsperf.com/thor-indexof-vs-for/5
957                indexOf = function( list, elem ) {
958                    var i = 0,
959                        len = list.length;
960                    for ( ; i < len; i++ ) {
961                        if ( list[ i ] === elem ) {
962                            return i;
963                        }
964                    }
965                    return -1;
966                },
967
968                booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
969                    "ismap|loop|multiple|open|readonly|required|scoped",
970
971                // Regular expressions
972
973                // http://www.w3.org/TR/css3-selectors/#whitespace
974                whitespace = "[\\x20\\t\\r\\n\\f]",
975
976                // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
977                identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
978                    "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
979
980                // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
981                attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
982
983                    // Operator (capture 2)
984                    "*([*^$|!~]?=)" + whitespace +
985
986                    // "Attribute values must be CSS identifiers [capture 5]
987                    // or strings [capture 3 or capture 4]"
988                    "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
989                    whitespace + "*\\]",
990
991                pseudos = ":(" + identifier + ")(?:\\((" +
992
993                    // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
994                    // 1. quoted (capture 3; capture 4 or capture 5)
995                    "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
996
997                    // 2. simple (capture 6)
998                    "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
999
1000                    // 3. anything else (capture 2)
1001                    ".*" +
1002                    ")\\)|)",
1003
1004                // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
1005                rwhitespace = new RegExp( whitespace + "+", "g" ),
1006                rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
1007                    whitespace + "+$", "g" ),
1008
1009                rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
1010                rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
1011                    "*" ),
1012                rdescend = new RegExp( whitespace + "|>" ),
1013
1014                rpseudo = new RegExp( pseudos ),
1015                ridentifier = new RegExp( "^" + identifier + "$" ),
1016
1017                matchExpr = {
1018                    "ID": new RegExp( "^#(" + identifier + ")" ),
1019                    "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
1020                    "TAG": new RegExp( "^(" + identifier + "|[*])" ),
1021                    "ATTR": new RegExp( "^" + attributes ),
1022                    "PSEUDO": new RegExp( "^" + pseudos ),
1023                    "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
1024                        whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
1025                        whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
1026                    "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
1027
1028                    // For use in libraries implementing .is()
1029                    // We use this for POS matching in `select`
1030                    "needsContext": new RegExp( "^" + whitespace +
1031                        "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
1032                        "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
1033                },
1034
1035                rhtml = /HTML$/i,
1036                rinputs = /^(?:input|select|textarea|button)$/i,
1037                rheader = /^h\d$/i,
1038
1039                rnative = /^[^{]+\{\s*\[native \w/,
1040
1041                // Easily-parseable/retrievable ID or TAG or CLASS selectors
1042                rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
1043
1044                rsibling = /[+~]/,
1045
1046                // CSS escapes
1047                // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
1048                runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
1049                funescape = function( escape, nonHex ) {
1050                    var high = "0x" + escape.slice( 1 ) - 0x10000;
1051
1052                    return nonHex ?
1053
1054                        // Strip the backslash prefix from a non-hex escape sequence
1055                        nonHex :
1056
1057                        // Replace a hexadecimal escape sequence with the encoded Unicode code point
1058                        // Support: IE <=11+
1059                        // For values outside the Basic Multilingual Plane (BMP), manually construct a
1060                        // surrogate pair
1061                        high < 0 ?
1062                            String.fromCharCode( high + 0x10000 ) :
1063                            String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
1064                },
1065
1066                // CSS string/identifier serialization
1067                // https://drafts.csswg.org/cssom/#common-serializing-idioms
1068                rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
1069                fcssescape = function( ch, asCodePoint ) {
1070                    if ( asCodePoint ) {
1071
1072                        // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
1073                        if ( ch === "\0" ) {
1074                            return "\uFFFD";
1075                        }
1076
1077                        // Control characters and (dependent upon position) numbers get escaped as code points
1078                        return ch.slice( 0, -1 ) + "\\" +
1079                            ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
1080                    }
1081
1082                    // Other potentially-special ASCII characters get backslash-escaped
1083                    return "\\" + ch;
1084                },
1085
1086                // Used for iframes
1087                // See setDocument()
1088                // Removing the function wrapper causes a "Permission Denied"
1089                // error in IE
1090                unloadHandler = function() {
1091                    setDocument();
1092                },
1093
1094                inDisabledFieldset = addCombinator(
1095                    function( elem ) {
1096                        return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
1097                    },
1098                    { dir: "parentNode", next: "legend" }
1099                );
1100
1101// Optimize for push.apply( _, NodeList )
1102            try {
1103                push.apply(
1104                    ( arr = slice.call( preferredDoc.childNodes ) ),
1105                    preferredDoc.childNodes
1106                );
1107
1108                // Support: Android<4.0
1109                // Detect silently failing push.apply
1110                // eslint-disable-next-line no-unused-expressions
1111                arr[ preferredDoc.childNodes.length ].nodeType;
1112            } catch ( e ) {
1113                push = { apply: arr.length ?
1114
1115                        // Leverage slice if possible
1116                        function( target, els ) {
1117                            pushNative.apply( target, slice.call( els ) );
1118                        } :
1119
1120                        // Support: IE<9
1121                        // Otherwise append directly
1122                        function( target, els ) {
1123                            var j = target.length,
1124                                i = 0;
1125
1126                            // Can't trust NodeList.length
1127                            while ( ( target[ j++ ] = els[ i++ ] ) ) {}
1128                            target.length = j - 1;
1129                        }
1130                };
1131            }
1132
1133            function Sizzle( selector, context, results, seed ) {
1134                var m, i, elem, nid, match, groups, newSelector,
1135                    newContext = context && context.ownerDocument,
1136
1137                    // nodeType defaults to 9, since context defaults to document
1138                    nodeType = context ? context.nodeType : 9;
1139
1140                results = results || [];
1141
1142                // Return early from calls with invalid selector or context
1143                if ( typeof selector !== "string" || !selector ||
1144                    nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
1145
1146                    return results;
1147                }
1148
1149                // Try to shortcut find operations (as opposed to filters) in HTML documents
1150                if ( !seed ) {
1151                    setDocument( context );
1152                    context = context || document;
1153
1154                    if ( documentIsHTML ) {
1155
1156                        // If the selector is sufficiently simple, try using a "get*By*" DOM method
1157                        // (excepting DocumentFragment context, where the methods don't exist)
1158                        if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
1159
1160                            // ID selector
1161                            if ( ( m = match[ 1 ] ) ) {
1162
1163                                // Document context
1164                                if ( nodeType === 9 ) {
1165                                    if ( ( elem = context.getElementById( m ) ) ) {
1166
1167                                        // Support: IE, Opera, Webkit
1168                                        // TODO: identify versions
1169                                        // getElementById can match elements by name instead of ID
1170                                        if ( elem.id === m ) {
1171                                            results.push( elem );
1172                                            return results;
1173                                        }
1174                                    } else {
1175                                        return results;
1176                                    }
1177
1178                                    // Element context
1179                                } else {
1180
1181                                    // Support: IE, Opera, Webkit
1182                                    // TODO: identify versions
1183                                    // getElementById can match elements by name instead of ID
1184                                    if ( newContext && ( elem = newContext.getElementById( m ) ) &&
1185                                        contains( context, elem ) &&
1186                                        elem.id === m ) {
1187
1188                                        results.push( elem );
1189                                        return results;
1190                                    }
1191                                }
1192
1193                                // Type selector
1194                            } else if ( match[ 2 ] ) {
1195                                push.apply( results, context.getElementsByTagName( selector ) );
1196                                return results;
1197
1198                                // Class selector
1199                            } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
1200                                context.getElementsByClassName ) {
1201
1202                                push.apply( results, context.getElementsByClassName( m ) );
1203                                return results;
1204                            }
1205                        }
1206
1207                        // Take advantage of querySelectorAll
1208                        if ( support.qsa &&
1209                            !nonnativeSelectorCache[ selector + " " ] &&
1210                            ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&
1211
1212                            // Support: IE 8 only
1213                            // Exclude object elements
1214                            ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {
1215
1216                            newSelector = selector;
1217                            newContext = context;
1218
1219                            // qSA considers elements outside a scoping root when evaluating child or
1220                            // descendant combinators, which is not what we want.
1221                            // In such cases, we work around the behavior by prefixing every selector in the
1222                            // list with an ID selector referencing the scope context.
1223                            // The technique has to be used as well when a leading combinator is used
1224                            // as such selectors are not recognized by querySelectorAll.
1225                            // Thanks to Andrew Dupont for this technique.
1226                            if ( nodeType === 1 &&
1227                                ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
1228
1229                                // Expand context for sibling selectors
1230                                newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
1231                                    context;
1232
1233                                // We can use :scope instead of the ID hack if the browser
1234                                // supports it & if we're not changing the context.
1235                                if ( newContext !== context || !support.scope ) {
1236
1237                                    // Capture the context ID, setting it first if necessary
1238                                    if ( ( nid = context.getAttribute( "id" ) ) ) {
1239                                        nid = nid.replace( rcssescape, fcssescape );
1240                                    } else {
1241                                        context.setAttribute( "id", ( nid = expando ) );
1242                                    }
1243                                }
1244
1245                                // Prefix every selector in the list
1246                                groups = tokenize( selector );
1247                                i = groups.length;
1248                                while ( i-- ) {
1249                                    groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
1250                                        toSelector( groups[ i ] );
1251                                }
1252                                newSelector = groups.join( "," );
1253                            }
1254
1255                            try {
1256                                push.apply( results,
1257                                    newContext.querySelectorAll( newSelector )
1258                                );
1259                                return results;
1260                            } catch ( qsaError ) {
1261                                nonnativeSelectorCache( selector, true );
1262                            } finally {
1263                                if ( nid === expando ) {
1264                                    context.removeAttribute( "id" );
1265                                }
1266                            }
1267                        }
1268                    }
1269                }
1270
1271                // All others
1272                return select( selector.replace( rtrim, "$1" ), context, results, seed );
1273            }
1274
1275            /**
1276             * Create key-value caches of limited size
1277             * @returns {function(string, object)} Returns the Object data after storing it on itself with
1278             *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
1279             *	deleting the oldest entry
1280             */
1281            function createCache() {
1282                var keys = [];
1283
1284                function cache( key, value ) {
1285
1286                    // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
1287                    if ( keys.push( key + " " ) > Expr.cacheLength ) {
1288
1289                        // Only keep the most recent entries
1290                        delete cache[ keys.shift() ];
1291                    }
1292                    return ( cache[ key + " " ] = value );
1293                }
1294                return cache;
1295            }
1296
1297            /**
1298             * Mark a function for special use by Sizzle
1299             * @param {Function} fn The function to mark
1300             */
1301            function markFunction( fn ) {
1302                fn[ expando ] = true;
1303                return fn;
1304            }
1305
1306            /**
1307             * Support testing using an element
1308             * @param {Function} fn Passed the created element and returns a boolean result
1309             */
1310            function assert( fn ) {
1311                var el = document.createElement( "fieldset" );
1312
1313                try {
1314                    return !!fn( el );
1315                } catch ( e ) {
1316                    return false;
vendor: 4,414 bytes, lines 1317-1424
1317                } finally {
1318
1319                    // Remove from its parent by default
1320                    if ( el.parentNode ) {
1321                        el.parentNode.removeChild( el );
1322                    }
1323
1324                    // release memory in IE
1325                    el = null;
1326                }
1327            }
1328
1329            /**
1330             * Adds the same handler for all of the specified attrs
1331             * @param {String} attrs Pipe-separated list of attributes
1332             * @param {Function} handler The method that will be applied
1333             */
1334            function addHandle( attrs, handler ) {
1335                var arr = attrs.split( "|" ),
1336                    i = arr.length;
1337
1338                while ( i-- ) {
1339                    Expr.attrHandle[ arr[ i ] ] = handler;
1340                }
1341            }
1342
1343            /**
1344             * Checks document order of two siblings
1345             * @param {Element} a
1346             * @param {Element} b
1347             * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
1348             */
1349            function siblingCheck( a, b ) {
1350                var cur = b && a,
1351                    diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
1352                        a.sourceIndex - b.sourceIndex;
1353
1354                // Use IE sourceIndex if available on both nodes
1355                if ( diff ) {
1356                    return diff;
1357                }
1358
1359                // Check if b follows a
1360                if ( cur ) {
1361                    while ( ( cur = cur.nextSibling ) ) {
1362                        if ( cur === b ) {
1363                            return -1;
1364                        }
1365                    }
1366                }
1367
1368                return a ? 1 : -1;
1369            }
1370
1371            /**
1372             * Returns a function to use in pseudos for input types
1373             * @param {String} type
1374             */
1375            function createInputPseudo( type ) {
1376                return function( elem ) {
1377                    var name = elem.nodeName.toLowerCase();
1378                    return name === "input" && elem.type === type;
1379                };
1380            }
1381
1382            /**
1383             * Returns a function to use in pseudos for buttons
1384             * @param {String} type
1385             */
1386            function createButtonPseudo( type ) {
1387                return function( elem ) {
1388                    var name = elem.nodeName.toLowerCase();
1389                    return ( name === "input" || name === "button" ) && elem.type === type;
1390                };
1391            }
1392
1393            /**
1394             * Returns a function to use in pseudos for :enabled/:disabled
1395             * @param {Boolean} disabled true for :disabled; false for :enabled
1396             */
1397            function createDisabledPseudo( disabled ) {
1398
1399                // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
1400                return function( elem ) {
1401
1402                    // Only certain elements can match :enabled or :disabled
1403                    // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
1404                    // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
1405                    if ( "form" in elem ) {
1406
1407                        // Check for inherited disabledness on relevant non-disabled elements:
1408                        // * listed form-associated elements in a disabled fieldset
1409                        //   https://html.spec.whatwg.org/multipage/forms.html#category-listed
1410                        //   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
1411                        // * option elements in a disabled optgroup
1412                        //   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
1413                        // All such elements have a "form" property.
1414                        if ( elem.parentNode && elem.disabled === false ) {
1415
1416                            // Option elements defer to a parent optgroup if present
1417                            if ( "label" in elem ) {
1418                                if ( "label" in elem.parentNode ) {
1419                                    return elem.parentNode.disabled === disabled;
1420                                } else {
1421                                    return elem.disabled === disabled;
1422                                }
1423                            }
1424
vendor: 32,617 bytes, lines 1425-2125
1425                            // Support: IE 6 - 11
1426                            // Use the isDisabled shortcut property to check for disabled fieldset ancestors
1427                            return elem.isDisabled === disabled ||
1428
1429                                // Where there is no isDisabled, check manually
1430                                /* jshint -W018 */
1431                                elem.isDisabled !== !disabled &&
1432                                inDisabledFieldset( elem ) === disabled;
1433                        }
1434
1435                        return elem.disabled === disabled;
1436
1437                        // Try to winnow out elements that can't be disabled before trusting the disabled property.
1438                        // Some victims get caught in our net (label, legend, menu, track), but it shouldn't
1439                        // even exist on them, let alone have a boolean value.
1440                    } else if ( "label" in elem ) {
1441                        return elem.disabled === disabled;
1442                    }
1443
1444                    // Remaining elements are neither :enabled nor :disabled
1445                    return false;
1446                };
1447            }
1448
1449            /**
1450             * Returns a function to use in pseudos for positionals
1451             * @param {Function} fn
1452             */
1453            function createPositionalPseudo( fn ) {
1454                return markFunction( function( argument ) {
1455                    argument = +argument;
1456                    return markFunction( function( seed, matches ) {
1457                        var j,
1458                            matchIndexes = fn( [], seed.length, argument ),
1459                            i = matchIndexes.length;
1460
1461                        // Match elements found at the specified indexes
1462                        while ( i-- ) {
1463                            if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
1464                                seed[ j ] = !( matches[ j ] = seed[ j ] );
1465                            }
1466                        }
1467                    } );
1468                } );
1469            }
1470
1471            /**
1472             * Checks a node for validity as a Sizzle context
1473             * @param {Element|Object=} context
1474             * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
1475             */
1476            function testContext( context ) {
1477                return context && typeof context.getElementsByTagName !== "undefined" && context;
1478            }
1479
1480// Expose support vars for convenience
1481            support = Sizzle.support = {};
1482
1483            /**
1484             * Detects XML nodes
1485             * @param {Element|Object} elem An element or a document
1486             * @returns {Boolean} True iff elem is a non-HTML XML node
1487             */
1488            isXML = Sizzle.isXML = function( elem ) {
1489                var namespace = elem && elem.namespaceURI,
1490                    docElem = elem && ( elem.ownerDocument || elem ).documentElement;
1491
1492                // Support: IE <=8
1493                // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
1494                // https://bugs.jquery.com/ticket/4833
1495                return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
1496            };
1497
1498            /**
1499             * Sets document-related variables once based on the current document
1500             * @param {Element|Object} [doc] An element or document object to use to set the document
1501             * @returns {Object} Returns the current document
1502             */
1503            setDocument = Sizzle.setDocument = function( node ) {
1504                var hasCompare, subWindow,
1505                    doc = node ? node.ownerDocument || node : preferredDoc;
1506
1507                // Return early if doc is invalid or already selected
1508                // Support: IE 11+, Edge 17 - 18+
1509                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1510                // two documents; shallow comparisons work.
1511                // eslint-disable-next-line eqeqeq
1512                if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
1513                    return document;
1514                }
1515
1516                // Update global variables
1517                document = doc;
1518                docElem = document.documentElement;
1519                documentIsHTML = !isXML( document );
1520
1521                // Support: IE 9 - 11+, Edge 12 - 18+
1522                // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
1523                // Support: IE 11+, Edge 17 - 18+
1524                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1525                // two documents; shallow comparisons work.
1526                // eslint-disable-next-line eqeqeq
1527                if ( preferredDoc != document &&
1528                    ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
1529
1530                    // Support: IE 11, Edge
1531                    if ( subWindow.addEventListener ) {
1532                        subWindow.addEventListener( "unload", unloadHandler, false );
1533
1534                        // Support: IE 9 - 10 only
1535                    } else if ( subWindow.attachEvent ) {
1536                        subWindow.attachEvent( "onunload", unloadHandler );
1537                    }
1538                }
1539
1540                // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
1541                // Safari 4 - 5 only, Opera <=11.6 - 12.x only
1542                // IE/Edge & older browsers don't support the :scope pseudo-class.
1543                // Support: Safari 6.0 only
1544                // Safari 6.0 supports :scope but it's an alias of :root there.
1545                support.scope = assert( function( el ) {
1546                    docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
1547                    return typeof el.querySelectorAll !== "undefined" &&
1548                        !el.querySelectorAll( ":scope fieldset div" ).length;
1549                } );
1550
1551                /* Attributes
1552	---------------------------------------------------------------------- */
1553
1554                // Support: IE<8
1555                // Verify that getAttribute really returns attributes and not properties
1556                // (excepting IE8 booleans)
1557                support.attributes = assert( function( el ) {
1558                    el.className = "i";
1559                    return !el.getAttribute( "className" );
1560                } );
1561
1562                /* getElement(s)By*
1563	---------------------------------------------------------------------- */
1564
1565                // Check if getElementsByTagName("*") returns only elements
1566                support.getElementsByTagName = assert( function( el ) {
1567                    el.appendChild( document.createComment( "" ) );
1568                    return !el.getElementsByTagName( "*" ).length;
1569                } );
1570
1571                // Support: IE<9
1572                support.getElementsByClassName = rnative.test( document.getElementsByClassName );
1573
1574                // Support: IE<10
1575                // Check if getElementById returns elements by name
1576                // The broken getElementById methods don't pick up programmatically-set names,
1577                // so use a roundabout getElementsByName test
1578                support.getById = assert( function( el ) {
1579                    docElem.appendChild( el ).id = expando;
1580                    return !document.getElementsByName || !document.getElementsByName( expando ).length;
1581                } );
1582
1583                // ID filter and find
1584                if ( support.getById ) {
1585                    Expr.filter[ "ID" ] = function( id ) {
1586                        var attrId = id.replace( runescape, funescape );
1587                        return function( elem ) {
1588                            return elem.getAttribute( "id" ) === attrId;
1589                        };
1590                    };
1591                    Expr.find[ "ID" ] = function( id, context ) {
1592                        if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
1593                            var elem = context.getElementById( id );
1594                            return elem ? [ elem ] : [];
1595                        }
1596                    };
1597                } else {
1598                    Expr.filter[ "ID" ] =  function( id ) {
1599                        var attrId = id.replace( runescape, funescape );
1600                        return function( elem ) {
1601                            var node = typeof elem.getAttributeNode !== "undefined" &&
1602                                elem.getAttributeNode( "id" );
1603                            return node && node.value === attrId;
1604                        };
1605                    };
1606
1607                    // Support: IE 6 - 7 only
1608                    // getElementById is not reliable as a find shortcut
1609                    Expr.find[ "ID" ] = function( id, context ) {
1610                        if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
1611                            var node, i, elems,
1612                                elem = context.getElementById( id );
1613
1614                            if ( elem ) {
1615
1616                                // Verify the id attribute
1617                                node = elem.getAttributeNode( "id" );
1618                                if ( node && node.value === id ) {
1619                                    return [ elem ];
1620                                }
1621
1622                                // Fall back on getElementsByName
1623                                elems = context.getElementsByName( id );
1624                                i = 0;
1625                                while ( ( elem = elems[ i++ ] ) ) {
1626                                    node = elem.getAttributeNode( "id" );
1627                                    if ( node && node.value === id ) {
1628                                        return [ elem ];
1629                                    }
1630                                }
1631                            }
1632
1633                            return [];
1634                        }
1635                    };
1636                }
1637
1638                // Tag
1639                Expr.find[ "TAG" ] = support.getElementsByTagName ?
1640                    function( tag, context ) {
1641                        if ( typeof context.getElementsByTagName !== "undefined" ) {
1642                            return context.getElementsByTagName( tag );
1643
1644                            // DocumentFragment nodes don't have gEBTN
1645                        } else if ( support.qsa ) {
1646                            return context.querySelectorAll( tag );
1647                        }
1648                    } :
1649
1650                    function( tag, context ) {
1651                        var elem,
1652                            tmp = [],
1653                            i = 0,
1654
1655                            // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
1656                            results = context.getElementsByTagName( tag );
1657
1658                        // Filter out possible comments
1659                        if ( tag === "*" ) {
1660                            while ( ( elem = results[ i++ ] ) ) {
1661                                if ( elem.nodeType === 1 ) {
1662                                    tmp.push( elem );
1663                                }
1664                            }
1665
1666                            return tmp;
1667                        }
1668                        return results;
1669                    };
1670
1671                // Class
1672                Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) {
1673                    if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
1674                        return context.getElementsByClassName( className );
1675                    }
1676                };
1677
1678                /* QSA/matchesSelector
1679	---------------------------------------------------------------------- */
1680
1681                // QSA and matchesSelector support
1682
1683                // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1684                rbuggyMatches = [];
1685
1686                // qSa(:focus) reports false when true (Chrome 21)
1687                // We allow this because of a bug in IE8/9 that throws an error
1688                // whenever `document.activeElement` is accessed on an iframe
1689                // So, we allow :focus to pass through QSA all the time to avoid the IE error
1690                // See https://bugs.jquery.com/ticket/13378
1691                rbuggyQSA = [];
1692
1693                if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {
1694
1695                    // Build QSA regex
1696                    // Regex strategy adopted from Diego Perini
1697                    assert( function( el ) {
1698
1699                        var input;
1700
1701                        // Select is set to empty string on purpose
1702                        // This is to test IE's treatment of not explicitly
1703                        // setting a boolean content attribute,
1704                        // since its presence should be enough
1705                        // https://bugs.jquery.com/ticket/12359
1706                        docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
1707                            "<select id='" + expando + "-\r\\' msallowcapture=''>" +
1708                            "<option selected=''></option></select>";
1709
1710                        // Support: IE8, Opera 11-12.16
1711                        // Nothing should be selected when empty strings follow ^= or $= or *=
1712                        // The test attribute must be unknown in Opera but "safe" for WinRT
1713                        // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
1714                        if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
1715                            rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
1716                        }
1717
1718                        // Support: IE8
1719                        // Boolean attributes and "value" are not treated correctly
1720                        if ( !el.querySelectorAll( "[selected]" ).length ) {
1721                            rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
1722                        }
1723
1724                        // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
1725                        if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
1726                            rbuggyQSA.push( "~=" );
1727                        }
1728
1729                        // Support: IE 11+, Edge 15 - 18+
1730                        // IE 11/Edge don't find elements on a `[name='']` query in some cases.
1731                        // Adding a temporary attribute to the document before the selection works
1732                        // around the issue.
1733                        // Interestingly, IE 10 & older don't seem to have the issue.
1734                        input = document.createElement( "input" );
1735                        input.setAttribute( "name", "" );
1736                        el.appendChild( input );
1737                        if ( !el.querySelectorAll( "[name='']" ).length ) {
1738                            rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
1739                                whitespace + "*(?:''|\"\")" );
1740                        }
1741
1742                        // Webkit/Opera - :checked should return selected option elements
1743                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1744                        // IE8 throws error here and will not see later tests
1745                        if ( !el.querySelectorAll( ":checked" ).length ) {
1746                            rbuggyQSA.push( ":checked" );
1747                        }
1748
1749                        // Support: Safari 8+, iOS 8+
1750                        // https://bugs.webkit.org/show_bug.cgi?id=136851
1751                        // In-page `selector#id sibling-combinator selector` fails
1752                        if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
1753                            rbuggyQSA.push( ".#.+[+~]" );
1754                        }
1755
1756                        // Support: Firefox <=3.6 - 5 only
1757                        // Old Firefox doesn't throw on a badly-escaped identifier.
1758                        el.querySelectorAll( "\\\f" );
1759                        rbuggyQSA.push( "[\\r\\n\\f]" );
1760                    } );
1761
1762                    assert( function( el ) {
1763                        el.innerHTML = "<a href='' disabled='disabled'></a>" +
1764                            "<select disabled='disabled'><option/></select>";
1765
1766                        // Support: Windows 8 Native Apps
1767                        // The type and name attributes are restricted during .innerHTML assignment
1768                        var input = document.createElement( "input" );
1769                        input.setAttribute( "type", "hidden" );
1770                        el.appendChild( input ).setAttribute( "name", "D" );
1771
1772                        // Support: IE8
1773                        // Enforce case-sensitivity of name attribute
1774                        if ( el.querySelectorAll( "[name=d]" ).length ) {
1775                            rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
1776                        }
1777
1778                        // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1779                        // IE8 throws error here and will not see later tests
1780                        if ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
1781                            rbuggyQSA.push( ":enabled", ":disabled" );
1782                        }
1783
1784                        // Support: IE9-11+
1785                        // IE's :disabled selector does not pick up the children of disabled fieldsets
1786                        docElem.appendChild( el ).disabled = true;
1787                        if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
1788                            rbuggyQSA.push( ":enabled", ":disabled" );
1789                        }
1790
1791                        // Support: Opera 10 - 11 only
1792                        // Opera 10-11 does not throw on post-comma invalid pseudos
1793                        el.querySelectorAll( "*,:x" );
1794                        rbuggyQSA.push( ",.*:" );
1795                    } );
1796                }
1797
1798                if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
1799                    docElem.webkitMatchesSelector ||
1800                    docElem.mozMatchesSelector ||
1801                    docElem.oMatchesSelector ||
1802                    docElem.msMatchesSelector ) ) ) ) {
1803
1804                    assert( function( el ) {
1805
1806                        // Check to see if it's possible to do matchesSelector
1807                        // on a disconnected node (IE 9)
1808                        support.disconnectedMatch = matches.call( el, "*" );
1809
1810                        // This should fail with an exception
1811                        // Gecko does not error, returns false instead
1812                        matches.call( el, "[s!='']:x" );
1813                        rbuggyMatches.push( "!=", pseudos );
1814                    } );
1815                }
1816
1817                rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
1818                rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );
1819
1820                /* Contains
1821	---------------------------------------------------------------------- */
1822                hasCompare = rnative.test( docElem.compareDocumentPosition );
1823
1824                // Element contains another
1825                // Purposefully self-exclusive
1826                // As in, an element does not contain itself
1827                contains = hasCompare || rnative.test( docElem.contains ) ?
1828                    function( a, b ) {
1829                        var adown = a.nodeType === 9 ? a.documentElement : a,
1830                            bup = b && b.parentNode;
1831                        return a === bup || !!( bup && bup.nodeType === 1 && (
1832                            adown.contains ?
1833                                adown.contains( bup ) :
1834                                a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1835                        ) );
1836                    } :
1837                    function( a, b ) {
1838                        if ( b ) {
1839                            while ( ( b = b.parentNode ) ) {
1840                                if ( b === a ) {
1841                                    return true;
1842                                }
1843                            }
1844                        }
1845                        return false;
1846                    };
1847
1848                /* Sorting
1849	---------------------------------------------------------------------- */
1850
1851                // Document order sorting
1852                sortOrder = hasCompare ?
1853                    function( a, b ) {
1854
1855                        // Flag for duplicate removal
1856                        if ( a === b ) {
1857                            hasDuplicate = true;
1858                            return 0;
1859                        }
1860
1861                        // Sort on method existence if only one input has compareDocumentPosition
1862                        var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
1863                        if ( compare ) {
1864                            return compare;
1865                        }
1866
1867                        // Calculate position if both inputs belong to the same document
1868                        // Support: IE 11+, Edge 17 - 18+
1869                        // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1870                        // two documents; shallow comparisons work.
1871                        // eslint-disable-next-line eqeqeq
1872                        compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
1873                            a.compareDocumentPosition( b ) :
1874
1875                            // Otherwise we know they are disconnected
1876                            1;
1877
1878                        // Disconnected nodes
1879                        if ( compare & 1 ||
1880                            ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
1881
1882                            // Choose the first element that is related to our preferred document
1883                            // Support: IE 11+, Edge 17 - 18+
1884                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1885                            // two documents; shallow comparisons work.
1886                            // eslint-disable-next-line eqeqeq
1887                            if ( a == document || a.ownerDocument == preferredDoc &&
1888                                contains( preferredDoc, a ) ) {
1889                                return -1;
1890                            }
1891
1892                            // Support: IE 11+, Edge 17 - 18+
1893                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1894                            // two documents; shallow comparisons work.
1895                            // eslint-disable-next-line eqeqeq
1896                            if ( b == document || b.ownerDocument == preferredDoc &&
1897                                contains( preferredDoc, b ) ) {
1898                                return 1;
1899                            }
1900
1901                            // Maintain original order
1902                            return sortInput ?
1903                                ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
1904                                0;
1905                        }
1906
1907                        return compare & 4 ? -1 : 1;
1908                    } :
1909                    function( a, b ) {
1910
1911                        // Exit early if the nodes are identical
1912                        if ( a === b ) {
1913                            hasDuplicate = true;
1914                            return 0;
1915                        }
1916
1917                        var cur,
1918                            i = 0,
1919                            aup = a.parentNode,
1920                            bup = b.parentNode,
1921                            ap = [ a ],
1922                            bp = [ b ];
1923
1924                        // Parentless nodes are either documents or disconnected
1925                        if ( !aup || !bup ) {
1926
1927                            // Support: IE 11+, Edge 17 - 18+
1928                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1929                            // two documents; shallow comparisons work.
1930                            /* eslint-disable eqeqeq */
1931                            return a == document ? -1 :
1932                                b == document ? 1 :
1933                                    /* eslint-enable eqeqeq */
1934                                    aup ? -1 :
1935                                        bup ? 1 :
1936                                            sortInput ?
1937                                                ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
1938                                                0;
1939
1940                            // If the nodes are siblings, we can do a quick check
1941                        } else if ( aup === bup ) {
1942                            return siblingCheck( a, b );
1943                        }
1944
1945                        // Otherwise we need full lists of their ancestors for comparison
1946                        cur = a;
1947                        while ( ( cur = cur.parentNode ) ) {
1948                            ap.unshift( cur );
1949                        }
1950                        cur = b;
1951                        while ( ( cur = cur.parentNode ) ) {
1952                            bp.unshift( cur );
1953                        }
1954
1955                        // Walk down the tree looking for a discrepancy
1956                        while ( ap[ i ] === bp[ i ] ) {
1957                            i++;
1958                        }
1959
1960                        return i ?
1961
1962                            // Do a sibling check if the nodes have a common ancestor
1963                            siblingCheck( ap[ i ], bp[ i ] ) :
1964
1965                            // Otherwise nodes in our document sort first
1966                            // Support: IE 11+, Edge 17 - 18+
1967                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1968                            // two documents; shallow comparisons work.
1969                            /* eslint-disable eqeqeq */
1970                            ap[ i ] == preferredDoc ? -1 :
1971                                bp[ i ] == preferredDoc ? 1 :
1972                                    /* eslint-enable eqeqeq */
1973                                    0;
1974                    };
1975
1976                return document;
1977            };
1978
1979            Sizzle.matches = function( expr, elements ) {
1980                return Sizzle( expr, null, null, elements );
1981            };
1982
1983            Sizzle.matchesSelector = function( elem, expr ) {
1984                setDocument( elem );
1985
1986                if ( support.matchesSelector && documentIsHTML &&
1987                    !nonnativeSelectorCache[ expr + " " ] &&
1988                    ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
1989                    ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
1990
1991                    try {
1992                        var ret = matches.call( elem, expr );
1993
1994                        // IE 9's matchesSelector returns false on disconnected nodes
1995                        if ( ret || support.disconnectedMatch ||
1996
1997                            // As well, disconnected nodes are said to be in a document
1998                            // fragment in IE 9
1999                            elem.document && elem.document.nodeType !== 11 ) {
2000                            return ret;
2001                        }
2002                    } catch ( e ) {
2003                        nonnativeSelectorCache( expr, true );
2004                    }
2005                }
2006
2007                return Sizzle( expr, document, null, [ elem ] ).length > 0;
2008            };
2009
2010            Sizzle.contains = function( context, elem ) {
2011
2012                // Set document vars if needed
2013                // Support: IE 11+, Edge 17 - 18+
2014                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2015                // two documents; shallow comparisons work.
2016                // eslint-disable-next-line eqeqeq
2017                if ( ( context.ownerDocument || context ) != document ) {
2018                    setDocument( context );
2019                }
2020                return contains( context, elem );
2021            };
2022
2023            Sizzle.attr = function( elem, name ) {
2024
2025                // Set document vars if needed
2026                // Support: IE 11+, Edge 17 - 18+
2027                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2028                // two documents; shallow comparisons work.
2029                // eslint-disable-next-line eqeqeq
2030                if ( ( elem.ownerDocument || elem ) != document ) {
2031                    setDocument( elem );
2032                }
2033
2034                var fn = Expr.attrHandle[ name.toLowerCase() ],
2035
2036                    // Don't get fooled by Object.prototype properties (jQuery #13807)
2037                    val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
2038                        fn( elem, name, !documentIsHTML ) :
2039                        undefined;
2040
2041                return val !== undefined ?
2042                    val :
2043                    support.attributes || !documentIsHTML ?
2044                        elem.getAttribute( name ) :
2045                        ( val = elem.getAttributeNode( name ) ) && val.specified ?
2046                            val.value :
2047                            null;
2048            };
2049
2050            Sizzle.escape = function( sel ) {
2051                return ( sel + "" ).replace( rcssescape, fcssescape );
2052            };
2053
2054            Sizzle.error = function( msg ) {
2055                throw new Error( "Syntax error, unrecognized expression: " + msg );
2056            };
2057
2058            /**
2059             * Document sorting and removing duplicates
2060             * @param {ArrayLike} results
2061             */
2062            Sizzle.uniqueSort = function( results ) {
2063                var elem,
2064                    duplicates = [],
2065                    j = 0,
2066                    i = 0;
2067
2068                // Unless we *know* we can detect duplicates, assume their presence
2069                hasDuplicate = !support.detectDuplicates;
2070                sortInput = !support.sortStable && results.slice( 0 );
2071                results.sort( sortOrder );
2072
2073                if ( hasDuplicate ) {
2074                    while ( ( elem = results[ i++ ] ) ) {
2075                        if ( elem === results[ i ] ) {
2076                            j = duplicates.push( i );
2077                        }
2078                    }
2079                    while ( j-- ) {
2080                        results.splice( duplicates[ j ], 1 );
2081                    }
2082                }
2083
2084                // Clear input after sorting to release objects
2085                // See https://github.com/jquery/sizzle/pull/225
2086                sortInput = null;
2087
2088                return results;
2089            };
2090
2091            /**
2092             * Utility function for retrieving the text value of an array of DOM nodes
2093             * @param {Array|Element} elem
2094             */
2095            getText = Sizzle.getText = function( elem ) {
2096                var node,
2097                    ret = "",
2098                    i = 0,
2099                    nodeType = elem.nodeType;
2100
2101                if ( !nodeType ) {
2102
2103                    // If no nodeType, this is expected to be an array
2104                    while ( ( node = elem[ i++ ] ) ) {
2105
2106                        // Do not traverse comment nodes
2107                        ret += getText( node );
2108                    }
2109                } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
2110
2111                    // Use textContent for elements
2112                    // innerText usage removed for consistency of new lines (jQuery #11153)
2113                    if ( typeof elem.textContent === "string" ) {
2114                        return elem.textContent;
2115                    } else {
2116
2117                        // Traverse its children
2118                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
2119                            ret += getText( elem );
2120                        }
2121                    }
2122                } else if ( nodeType === 3 || nodeType === 4 ) {
2123                    return elem.nodeValue;
2124                }
2125
vendor: 13,229 bytes, lines 2126-2400
2126                // Do not include comment or processing instruction nodes
2127
2128                return ret;
2129            };
2130
2131            Expr = Sizzle.selectors = {
2132
2133                // Can be adjusted by the user
2134                cacheLength: 50,
2135
2136                createPseudo: markFunction,
2137
2138                match: matchExpr,
2139
2140                attrHandle: {},
2141
2142                find: {},
2143
2144                relative: {
2145                    ">": { dir: "parentNode", first: true },
2146                    " ": { dir: "parentNode" },
2147                    "+": { dir: "previousSibling", first: true },
2148                    "~": { dir: "previousSibling" }
2149                },
2150
2151                preFilter: {
2152                    "ATTR": function( match ) {
2153                        match[ 1 ] = match[ 1 ].replace( runescape, funescape );
2154
2155                        // Move the given value to match[3] whether quoted or unquoted
2156                        match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
2157                            match[ 5 ] || "" ).replace( runescape, funescape );
2158
2159                        if ( match[ 2 ] === "~=" ) {
2160                            match[ 3 ] = " " + match[ 3 ] + " ";
2161                        }
2162
2163                        return match.slice( 0, 4 );
2164                    },
2165
2166                    "CHILD": function( match ) {
2167
2168                        /* matches from matchExpr["CHILD"]
2169				1 type (only|nth|...)
2170				2 what (child|of-type)
2171				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
2172				4 xn-component of xn+y argument ([+-]?\d*n|)
2173				5 sign of xn-component
2174				6 x of xn-component
2175				7 sign of y-component
2176				8 y of y-component
2177			*/
2178                        match[ 1 ] = match[ 1 ].toLowerCase();
2179
2180                        if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
2181
2182                            // nth-* requires argument
2183                            if ( !match[ 3 ] ) {
2184                                Sizzle.error( match[ 0 ] );
2185                            }
2186
2187                            // numeric x and y parameters for Expr.filter.CHILD
2188                            // remember that false/true cast respectively to 0/1
2189                            match[ 4 ] = +( match[ 4 ] ?
2190                                match[ 5 ] + ( match[ 6 ] || 1 ) :
2191                                2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
2192                            match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
2193
2194                            // other types prohibit arguments
2195                        } else if ( match[ 3 ] ) {
2196                            Sizzle.error( match[ 0 ] );
2197                        }
2198
2199                        return match;
2200                    },
2201
2202                    "PSEUDO": function( match ) {
2203                        var excess,
2204                            unquoted = !match[ 6 ] && match[ 2 ];
2205
2206                        if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
2207                            return null;
2208                        }
2209
2210                        // Accept quoted arguments as-is
2211                        if ( match[ 3 ] ) {
2212                            match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
2213
2214                            // Strip excess characters from unquoted arguments
2215                        } else if ( unquoted && rpseudo.test( unquoted ) &&
2216
2217                            // Get excess from tokenize (recursively)
2218                            ( excess = tokenize( unquoted, true ) ) &&
2219
2220                            // advance to the next closing parenthesis
2221                            ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
2222
2223                            // excess is a negative index
2224                            match[ 0 ] = match[ 0 ].slice( 0, excess );
2225                            match[ 2 ] = unquoted.slice( 0, excess );
2226                        }
2227
2228                        // Return only captures needed by the pseudo filter method (type and argument)
2229                        return match.slice( 0, 3 );
2230                    }
2231                },
2232
2233                filter: {
2234
2235                    "TAG": function( nodeNameSelector ) {
2236                        var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
2237                        return nodeNameSelector === "*" ?
2238                            function() {
2239                                return true;
2240                            } :
2241                            function( elem ) {
2242                                return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
2243                            };
2244                    },
2245
2246                    "CLASS": function( className ) {
2247                        var pattern = classCache[ className + " " ];
2248
2249                        return pattern ||
2250                            ( pattern = new RegExp( "(^|" + whitespace +
2251                                ")" + className + "(" + whitespace + "|$)" ) ) && classCache(
2252                                className, function( elem ) {
2253                                    return pattern.test(
2254                                        typeof elem.className === "string" && elem.className ||
2255                                        typeof elem.getAttribute !== "undefined" &&
2256                                        elem.getAttribute( "class" ) ||
2257                                        ""
2258                                    );
2259                                } );
2260                    },
2261
2262                    "ATTR": function( name, operator, check ) {
2263                        return function( elem ) {
2264                            var result = Sizzle.attr( elem, name );
2265
2266                            if ( result == null ) {
2267                                return operator === "!=";
2268                            }
2269                            if ( !operator ) {
2270                                return true;
2271                            }
2272
2273                            result += "";
2274
2275                            /* eslint-disable max-len */
2276
2277                            return operator === "=" ? result === check :
2278                                operator === "!=" ? result !== check :
2279                                    operator === "^=" ? check && result.indexOf( check ) === 0 :
2280                                        operator === "*=" ? check && result.indexOf( check ) > -1 :
2281                                            operator === "$=" ? check && result.slice( -check.length ) === check :
2282                                                operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
2283                                                    operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
2284                                                        false;
2285                            /* eslint-enable max-len */
2286
2287                        };
2288                    },
2289
2290                    "CHILD": function( type, what, _argument, first, last ) {
2291                        var simple = type.slice( 0, 3 ) !== "nth",
2292                            forward = type.slice( -4 ) !== "last",
2293                            ofType = what === "of-type";
2294
2295                        return first === 1 && last === 0 ?
2296
2297                            // Shortcut for :nth-*(n)
2298                            function( elem ) {
2299                                return !!elem.parentNode;
2300                            } :
2301
2302                            function( elem, _context, xml ) {
2303                                var cache, uniqueCache, outerCache, node, nodeIndex, start,
2304                                    dir = simple !== forward ? "nextSibling" : "previousSibling",
2305                                    parent = elem.parentNode,
2306                                    name = ofType && elem.nodeName.toLowerCase(),
2307                                    useCache = !xml && !ofType,
2308                                    diff = false;
2309
2310                                if ( parent ) {
2311
2312                                    // :(first|last|only)-(child|of-type)
2313                                    if ( simple ) {
2314                                        while ( dir ) {
2315                                            node = elem;
2316                                            while ( ( node = node[ dir ] ) ) {
2317                                                if ( ofType ?
2318                                                    node.nodeName.toLowerCase() === name :
2319                                                    node.nodeType === 1 ) {
2320
2321                                                    return false;
2322                                                }
2323                                            }
2324
2325                                            // Reverse direction for :only-* (if we haven't yet done so)
2326                                            start = dir = type === "only" && !start && "nextSibling";
2327                                        }
2328                                        return true;
2329                                    }
2330
2331                                    start = [ forward ? parent.firstChild : parent.lastChild ];
2332
2333                                    // non-xml :nth-child(...) stores cache data on `parent`
2334                                    if ( forward && useCache ) {
2335
2336                                        // Seek `elem` from a previously-cached index
2337
2338                                        // ...in a gzip-friendly way
2339                                        node = parent;
2340                                        outerCache = node[ expando ] || ( node[ expando ] = {} );
2341
2342                                        // Support: IE <9 only
2343                                        // Defend against cloned attroperties (jQuery gh-1709)
2344                                        uniqueCache = outerCache[ node.uniqueID ] ||
2345                                            ( outerCache[ node.uniqueID ] = {} );
2346
2347                                        cache = uniqueCache[ type ] || [];
2348                                        nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
2349                                        diff = nodeIndex && cache[ 2 ];
2350                                        node = nodeIndex && parent.childNodes[ nodeIndex ];
2351
2352                                        while ( ( node = ++nodeIndex && node && node[ dir ] ||
2353
2354                                            // Fallback to seeking `elem` from the start
2355                                            ( diff = nodeIndex = 0 ) || start.pop() ) ) {
2356
2357                                            // When found, cache indexes on `parent` and break
2358                                            if ( node.nodeType === 1 && ++diff && node === elem ) {
2359                                                uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
2360                                                break;
2361                                            }
2362                                        }
2363
2364                                    } else {
2365
2366                                        // Use previously-cached element index if available
2367                                        if ( useCache ) {
2368
2369                                            // ...in a gzip-friendly way
2370                                            node = elem;
2371                                            outerCache = node[ expando ] || ( node[ expando ] = {} );
2372
2373                                            // Support: IE <9 only
2374                                            // Defend against cloned attroperties (jQuery gh-1709)
2375                                            uniqueCache = outerCache[ node.uniqueID ] ||
2376                                                ( outerCache[ node.uniqueID ] = {} );
2377
2378                                            cache = uniqueCache[ type ] || [];
2379                                            nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
2380                                            diff = nodeIndex;
2381                                        }
2382
2383                                        // xml :nth-child(...)
2384                                        // or :nth-last-child(...) or :nth(-last)?-of-type(...)
2385                                        if ( diff === false ) {
2386
2387                                            // Use the same loop as above to seek `elem` from the start
2388                                            while ( ( node = ++nodeIndex && node && node[ dir ] ||
2389                                                ( diff = nodeIndex = 0 ) || start.pop() ) ) {
2390
2391                                                if ( ( ofType ?
2392                                                    node.nodeName.toLowerCase() === name :
2393                                                    node.nodeType === 1 ) &&
2394                                                    ++diff ) {
2395
2396                                                    // Cache the index of each encountered element
2397                                                    if ( useCache ) {
2398                                                        outerCache = node[ expando ] ||
2399                                                            ( node[ expando ] = {} );
2400
vendor: 14,491 bytes, lines 2401-2716
2401                                                        // Support: IE <9 only
2402                                                        // Defend against cloned attroperties (jQuery gh-1709)
2403                                                        uniqueCache = outerCache[ node.uniqueID ] ||
2404                                                            ( outerCache[ node.uniqueID ] = {} );
2405
2406                                                        uniqueCache[ type ] = [ dirruns, diff ];
2407                                                    }
2408
2409                                                    if ( node === elem ) {
2410                                                        break;
2411                                                    }
2412                                                }
2413                                            }
2414                                        }
2415                                    }
2416
2417                                    // Incorporate the offset, then check against cycle size
2418                                    diff -= last;
2419                                    return diff === first || ( diff % first === 0 && diff / first >= 0 );
2420                                }
2421                            };
2422                    },
2423
2424                    "PSEUDO": function( pseudo, argument ) {
2425
2426                        // pseudo-class names are case-insensitive
2427                        // http://www.w3.org/TR/selectors/#pseudo-classes
2428                        // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
2429                        // Remember that setFilters inherits from pseudos
2430                        var args,
2431                            fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
2432                                Sizzle.error( "unsupported pseudo: " + pseudo );
2433
2434                        // The user may use createPseudo to indicate that
2435                        // arguments are needed to create the filter function
2436                        // just as Sizzle does
2437                        if ( fn[ expando ] ) {
2438                            return fn( argument );
2439                        }
2440
2441                        // But maintain support for old signatures
2442                        if ( fn.length > 1 ) {
2443                            args = [ pseudo, pseudo, "", argument ];
2444                            return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
2445                                markFunction( function( seed, matches ) {
2446                                    var idx,
2447                                        matched = fn( seed, argument ),
2448                                        i = matched.length;
2449                                    while ( i-- ) {
2450                                        idx = indexOf( seed, matched[ i ] );
2451                                        seed[ idx ] = !( matches[ idx ] = matched[ i ] );
2452                                    }
2453                                } ) :
2454                                function( elem ) {
2455                                    return fn( elem, 0, args );
2456                                };
2457                        }
2458
2459                        return fn;
2460                    }
2461                },
2462
2463                pseudos: {
2464
2465                    // Potentially complex pseudos
2466                    "not": markFunction( function( selector ) {
2467
2468                        // Trim the selector passed to compile
2469                        // to avoid treating leading and trailing
2470                        // spaces as combinators
2471                        var input = [],
2472                            results = [],
2473                            matcher = compile( selector.replace( rtrim, "$1" ) );
2474
2475                        return matcher[ expando ] ?
2476                            markFunction( function( seed, matches, _context, xml ) {
2477                                var elem,
2478                                    unmatched = matcher( seed, null, xml, [] ),
2479                                    i = seed.length;
2480
2481                                // Match elements unmatched by `matcher`
2482                                while ( i-- ) {
2483                                    if ( ( elem = unmatched[ i ] ) ) {
2484                                        seed[ i ] = !( matches[ i ] = elem );
2485                                    }
2486                                }
2487                            } ) :
2488                            function( elem, _context, xml ) {
2489                                input[ 0 ] = elem;
2490                                matcher( input, null, xml, results );
2491
2492                                // Don't keep the element (issue #299)
2493                                input[ 0 ] = null;
2494                                return !results.pop();
2495                            };
2496                    } ),
2497
2498                    "has": markFunction( function( selector ) {
2499                        return function( elem ) {
2500                            return Sizzle( selector, elem ).length > 0;
2501                        };
2502                    } ),
2503
2504                    "contains": markFunction( function( text ) {
2505                        text = text.replace( runescape, funescape );
2506                        return function( elem ) {
2507                            return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
2508                        };
2509                    } ),
2510
2511                    // "Whether an element is represented by a :lang() selector
2512                    // is based solely on the element's language value
2513                    // being equal to the identifier C,
2514                    // or beginning with the identifier C immediately followed by "-".
2515                    // The matching of C against the element's language value is performed case-insensitively.
2516                    // The identifier C does not have to be a valid language name."
2517                    // http://www.w3.org/TR/selectors/#lang-pseudo
2518                    "lang": markFunction( function( lang ) {
2519
2520                        // lang value must be a valid identifier
2521                        if ( !ridentifier.test( lang || "" ) ) {
2522                            Sizzle.error( "unsupported lang: " + lang );
2523                        }
2524                        lang = lang.replace( runescape, funescape ).toLowerCase();
2525                        return function( elem ) {
2526                            var elemLang;
2527                            do {
2528                                if ( ( elemLang = documentIsHTML ?
2529                                    elem.lang :
2530                                    elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
2531
2532                                    elemLang = elemLang.toLowerCase();
2533                                    return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
2534                                }
2535                            } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
2536                            return false;
2537                        };
2538                    } ),
2539
2540                    // Miscellaneous
2541                    "target": function( elem ) {
2542                        var hash = window.location && window.location.hash;
2543                        return hash && hash.slice( 1 ) === elem.id;
2544                    },
2545
2546                    "root": function( elem ) {
2547                        return elem === docElem;
2548                    },
2549
2550                    "focus": function( elem ) {
2551                        return elem === document.activeElement &&
2552                            ( !document.hasFocus || document.hasFocus() ) &&
2553                            !!( elem.type || elem.href || ~elem.tabIndex );
2554                    },
2555
2556                    // Boolean properties
2557                    "enabled": createDisabledPseudo( false ),
2558                    "disabled": createDisabledPseudo( true ),
2559
2560                    "checked": function( elem ) {
2561
2562                        // In CSS3, :checked should return both checked and selected elements
2563                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
2564                        var nodeName = elem.nodeName.toLowerCase();
2565                        return ( nodeName === "input" && !!elem.checked ) ||
2566                            ( nodeName === "option" && !!elem.selected );
2567                    },
2568
2569                    "selected": function( elem ) {
2570
2571                        // Accessing this property makes selected-by-default
2572                        // options in Safari work properly
2573                        if ( elem.parentNode ) {
2574                            // eslint-disable-next-line no-unused-expressions
2575                            elem.parentNode.selectedIndex;
2576                        }
2577
2578                        return elem.selected === true;
2579                    },
2580
2581                    // Contents
2582                    "empty": function( elem ) {
2583
2584                        // http://www.w3.org/TR/selectors/#empty-pseudo
2585                        // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
2586                        //   but not by others (comment: 8; processing instruction: 7; etc.)
2587                        // nodeType < 6 works because attributes (2) do not appear as children
2588                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
2589                            if ( elem.nodeType < 6 ) {
2590                                return false;
2591                            }
2592                        }
2593                        return true;
2594                    },
2595
2596                    "parent": function( elem ) {
2597                        return !Expr.pseudos[ "empty" ]( elem );
2598                    },
2599
2600                    // Element/input types
2601                    "header": function( elem ) {
2602                        return rheader.test( elem.nodeName );
2603                    },
2604
2605                    "input": function( elem ) {
2606                        return rinputs.test( elem.nodeName );
2607                    },
2608
2609                    "button": function( elem ) {
2610                        var name = elem.nodeName.toLowerCase();
2611                        return name === "input" && elem.type === "button" || name === "button";
2612                    },
2613
2614                    "text": function( elem ) {
2615                        var attr;
2616                        return elem.nodeName.toLowerCase() === "input" &&
2617                            elem.type === "text" &&
2618
2619                            // Support: IE<8
2620                            // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
2621                            ( ( attr = elem.getAttribute( "type" ) ) == null ||
2622                                attr.toLowerCase() === "text" );
2623                    },
2624
2625                    // Position-in-collection
2626                    "first": createPositionalPseudo( function() {
2627                        return [ 0 ];
2628                    } ),
2629
2630                    "last": createPositionalPseudo( function( _matchIndexes, length ) {
2631                        return [ length - 1 ];
2632                    } ),
2633
2634                    "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
2635                        return [ argument < 0 ? argument + length : argument ];
2636                    } ),
2637
2638                    "even": createPositionalPseudo( function( matchIndexes, length ) {
2639                        var i = 0;
2640                        for ( ; i < length; i += 2 ) {
2641                            matchIndexes.push( i );
2642                        }
2643                        return matchIndexes;
2644                    } ),
2645
2646                    "odd": createPositionalPseudo( function( matchIndexes, length ) {
2647                        var i = 1;
2648                        for ( ; i < length; i += 2 ) {
2649                            matchIndexes.push( i );
2650                        }
2651                        return matchIndexes;
2652                    } ),
2653
2654                    "lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
2655                        var i = argument < 0 ?
2656                            argument + length :
2657                            argument > length ?
2658                                length :
2659                                argument;
2660                        for ( ; --i >= 0; ) {
2661                            matchIndexes.push( i );
2662                        }
2663                        return matchIndexes;
2664                    } ),
2665
2666                    "gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
2667                        var i = argument < 0 ? argument + length : argument;
2668                        for ( ; ++i < length; ) {
2669                            matchIndexes.push( i );
2670                        }
2671                        return matchIndexes;
2672                    } )
2673                }
2674            };
2675
2676            Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ];
2677
2678// Add button/input type pseudos
2679            for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
2680                Expr.pseudos[ i ] = createInputPseudo( i );
2681            }
2682            for ( i in { submit: true, reset: true } ) {
2683                Expr.pseudos[ i ] = createButtonPseudo( i );
2684            }
2685
2686// Easy API for creating new setFilters
2687            function setFilters() {}
2688            setFilters.prototype = Expr.filters = Expr.pseudos;
2689            Expr.setFilters = new setFilters();
2690
2691            tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
2692                var matched, match, tokens, type,
2693                    soFar, groups, preFilters,
2694                    cached = tokenCache[ selector + " " ];
2695
2696                if ( cached ) {
2697                    return parseOnly ? 0 : cached.slice( 0 );
2698                }
2699
2700                soFar = selector;
2701                groups = [];
2702                preFilters = Expr.preFilter;
2703
2704                while ( soFar ) {
2705
2706                    // Comma and first run
2707                    if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
2708                        if ( match ) {
2709
2710                            // Don't consume trailing commas as valid
2711                            soFar = soFar.slice( match[ 0 ].length ) || soFar;
2712                        }
2713                        groups.push( ( tokens = [] ) );
2714                    }
2715
2716                    matched = false;
vendor: 5,359 bytes, lines 2717-2834
2717
2718                    // Combinators
2719                    if ( ( match = rcombinators.exec( soFar ) ) ) {
2720                        matched = match.shift();
2721                        tokens.push( {
2722                            value: matched,
2723
2724                            // Cast descendant combinators to space
2725                            type: match[ 0 ].replace( rtrim, " " )
2726                        } );
2727                        soFar = soFar.slice( matched.length );
2728                    }
2729
2730                    // Filters
2731                    for ( type in Expr.filter ) {
2732                        if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
2733                            ( match = preFilters[ type ]( match ) ) ) ) {
2734                            matched = match.shift();
2735                            tokens.push( {
2736                                value: matched,
2737                                type: type,
2738                                matches: match
2739                            } );
2740                            soFar = soFar.slice( matched.length );
2741                        }
2742                    }
2743
2744                    if ( !matched ) {
2745                        break;
2746                    }
2747                }
2748
2749                // Return the length of the invalid excess
2750                // if we're just parsing
2751                // Otherwise, throw an error or return tokens
2752                return parseOnly ?
2753                    soFar.length :
2754                    soFar ?
2755                        Sizzle.error( selector ) :
2756
2757                        // Cache the tokens
2758                        tokenCache( selector, groups ).slice( 0 );
2759            };
2760
2761            function toSelector( tokens ) {
2762                var i = 0,
2763                    len = tokens.length,
2764                    selector = "";
2765                for ( ; i < len; i++ ) {
2766                    selector += tokens[ i ].value;
2767                }
2768                return selector;
2769            }
2770
2771            function addCombinator( matcher, combinator, base ) {
2772                var dir = combinator.dir,
2773                    skip = combinator.next,
2774                    key = skip || dir,
2775                    checkNonElements = base && key === "parentNode",
2776                    doneName = done++;
2777
2778                return combinator.first ?
2779
2780                    // Check against closest ancestor/preceding element
2781                    function( elem, context, xml ) {
2782                        while ( ( elem = elem[ dir ] ) ) {
2783                            if ( elem.nodeType === 1 || checkNonElements ) {
2784                                return matcher( elem, context, xml );
2785                            }
2786                        }
2787                        return false;
2788                    } :
2789
2790                    // Check against all ancestor/preceding elements
2791                    function( elem, context, xml ) {
2792                        var oldCache, uniqueCache, outerCache,
2793                            newCache = [ dirruns, doneName ];
2794
2795                        // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
2796                        if ( xml ) {
2797                            while ( ( elem = elem[ dir ] ) ) {
2798                                if ( elem.nodeType === 1 || checkNonElements ) {
2799                                    if ( matcher( elem, context, xml ) ) {
2800                                        return true;
2801                                    }
2802                                }
2803                            }
2804                        } else {
2805                            while ( ( elem = elem[ dir ] ) ) {
2806                                if ( elem.nodeType === 1 || checkNonElements ) {
2807                                    outerCache = elem[ expando ] || ( elem[ expando ] = {} );
2808
2809                                    // Support: IE <9 only
2810                                    // Defend against cloned attroperties (jQuery gh-1709)
2811                                    uniqueCache = outerCache[ elem.uniqueID ] ||
2812                                        ( outerCache[ elem.uniqueID ] = {} );
2813
2814                                    if ( skip && skip === elem.nodeName.toLowerCase() ) {
2815                                        elem = elem[ dir ] || elem;
2816                                    } else if ( ( oldCache = uniqueCache[ key ] ) &&
2817                                        oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
2818
2819                                        // Assign to newCache so results back-propagate to previous elements
2820                                        return ( newCache[ 2 ] = oldCache[ 2 ] );
2821                                    } else {
2822
2823                                        // Reuse newcache so results back-propagate to previous elements
2824                                        uniqueCache[ key ] = newCache;
2825
2826                                        // A match means we're done; a fail means we have to keep checking
2827                                        if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
2828                                            return true;
2829                                        }
2830                                    }
2831                                }
2832                            }
2833                        }
2834                        return false;
vendor: 8,930 bytes, lines 2835-3031
2835                    };
2836            }
2837
2838            function elementMatcher( matchers ) {
2839                return matchers.length > 1 ?
2840                    function( elem, context, xml ) {
2841                        var i = matchers.length;
2842                        while ( i-- ) {
2843                            if ( !matchers[ i ]( elem, context, xml ) ) {
2844                                return false;
2845                            }
2846                        }
2847                        return true;
2848                    } :
2849                    matchers[ 0 ];
2850            }
2851
2852            function multipleContexts( selector, contexts, results ) {
2853                var i = 0,
2854                    len = contexts.length;
2855                for ( ; i < len; i++ ) {
2856                    Sizzle( selector, contexts[ i ], results );
2857                }
2858                return results;
2859            }
2860
2861            function condense( unmatched, map, filter, context, xml ) {
2862                var elem,
2863                    newUnmatched = [],
2864                    i = 0,
2865                    len = unmatched.length,
2866                    mapped = map != null;
2867
2868                for ( ; i < len; i++ ) {
2869                    if ( ( elem = unmatched[ i ] ) ) {
2870                        if ( !filter || filter( elem, context, xml ) ) {
2871                            newUnmatched.push( elem );
2872                            if ( mapped ) {
2873                                map.push( i );
2874                            }
2875                        }
2876                    }
2877                }
2878
2879                return newUnmatched;
2880            }
2881
2882            function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2883                if ( postFilter && !postFilter[ expando ] ) {
2884                    postFilter = setMatcher( postFilter );
2885                }
2886                if ( postFinder && !postFinder[ expando ] ) {
2887                    postFinder = setMatcher( postFinder, postSelector );
2888                }
2889                return markFunction( function( seed, results, context, xml ) {
2890                    var temp, i, elem,
2891                        preMap = [],
2892                        postMap = [],
2893                        preexisting = results.length,
2894
2895                        // Get initial elements from seed or context
2896                        elems = seed || multipleContexts(
2897                            selector || "*",
2898                            context.nodeType ? [ context ] : context,
2899                            []
2900                        ),
2901
2902                        // Prefilter to get matcher input, preserving a map for seed-results synchronization
2903                        matcherIn = preFilter && ( seed || !selector ) ?
2904                            condense( elems, preMap, preFilter, context, xml ) :
2905                            elems,
2906
2907                        matcherOut = matcher ?
2908
2909                            // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2910                            postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2911
2912                                // ...intermediate processing is necessary
2913                                [] :
2914
2915                                // ...otherwise use results directly
2916                                results :
2917                            matcherIn;
2918
2919                    // Find primary matches
2920                    if ( matcher ) {
2921                        matcher( matcherIn, matcherOut, context, xml );
2922                    }
2923
2924                    // Apply postFilter
2925                    if ( postFilter ) {
2926                        temp = condense( matcherOut, postMap );
2927                        postFilter( temp, [], context, xml );
2928
2929                        // Un-match failing elements by moving them back to matcherIn
2930                        i = temp.length;
2931                        while ( i-- ) {
2932                            if ( ( elem = temp[ i ] ) ) {
2933                                matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
2934                            }
2935                        }
2936                    }
2937
2938                    if ( seed ) {
2939                        if ( postFinder || preFilter ) {
2940                            if ( postFinder ) {
2941
2942                                // Get the final matcherOut by condensing this intermediate into postFinder contexts
2943                                temp = [];
2944                                i = matcherOut.length;
2945                                while ( i-- ) {
2946                                    if ( ( elem = matcherOut[ i ] ) ) {
2947
2948                                        // Restore matcherIn since elem is not yet a final match
2949                                        temp.push( ( matcherIn[ i ] = elem ) );
2950                                    }
2951                                }
2952                                postFinder( null, ( matcherOut = [] ), temp, xml );
2953                            }
2954
2955                            // Move matched elements from seed to results to keep them synchronized
2956                            i = matcherOut.length;
2957                            while ( i-- ) {
2958                                if ( ( elem = matcherOut[ i ] ) &&
2959                                    ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {
2960
2961                                    seed[ temp ] = !( results[ temp ] = elem );
2962                                }
2963                            }
2964                        }
2965
2966                        // Add elements to results, through postFinder if defined
2967                    } else {
2968                        matcherOut = condense(
2969                            matcherOut === results ?
2970                                matcherOut.splice( preexisting, matcherOut.length ) :
2971                                matcherOut
2972                        );
2973                        if ( postFinder ) {
2974                            postFinder( null, results, matcherOut, xml );
2975                        } else {
2976                            push.apply( results, matcherOut );
2977                        }
2978                    }
2979                } );
2980            }
2981
2982            function matcherFromTokens( tokens ) {
2983                var checkContext, matcher, j,
2984                    len = tokens.length,
2985                    leadingRelative = Expr.relative[ tokens[ 0 ].type ],
2986                    implicitRelative = leadingRelative || Expr.relative[ " " ],
2987                    i = leadingRelative ? 1 : 0,
2988
2989                    // The foundational matcher ensures that elements are reachable from top-level context(s)
2990                    matchContext = addCombinator( function( elem ) {
2991                        return elem === checkContext;
2992                    }, implicitRelative, true ),
2993                    matchAnyContext = addCombinator( function( elem ) {
2994                        return indexOf( checkContext, elem ) > -1;
2995                    }, implicitRelative, true ),
2996                    matchers = [ function( elem, context, xml ) {
2997                        var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2998                            ( checkContext = context ).nodeType ?
2999                                matchContext( elem, context, xml ) :
3000                                matchAnyContext( elem, context, xml ) );
3001
3002                        // Avoid hanging onto element (issue #299)
3003                        checkContext = null;
3004                        return ret;
3005                    } ];
3006
3007                for ( ; i < len; i++ ) {
3008                    if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
3009                        matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
3010                    } else {
3011                        matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
3012
3013                        // Return special upon seeing a positional matcher
3014                        if ( matcher[ expando ] ) {
3015
3016                            // Find the next relative operator (if any) for proper handling
3017                            j = ++i;
3018                            for ( ; j < len; j++ ) {
3019                                if ( Expr.relative[ tokens[ j ].type ] ) {
3020                                    break;
3021                                }
3022                            }
3023                            return setMatcher(
3024                                i > 1 && elementMatcher( matchers ),
3025                                i > 1 && toSelector(
3026
3027                                // If the preceding token was a descendant combinator, insert an implicit any-element `*`
3028                                tokens
3029                                    .slice( 0, i - 1 )
3030                                    .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
3031                                ).replace( rtrim, "$1" ),
vendor: 17,588 bytes, lines 3032-3460
3032                                matcher,
3033                                i < j && matcherFromTokens( tokens.slice( i, j ) ),
3034                                j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
3035                                j < len && toSelector( tokens )
3036                            );
3037                        }
3038                        matchers.push( matcher );
3039                    }
3040                }
3041
3042                return elementMatcher( matchers );
3043            }
3044
3045            function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
3046                var bySet = setMatchers.length > 0,
3047                    byElement = elementMatchers.length > 0,
3048                    superMatcher = function( seed, context, xml, results, outermost ) {
3049                        var elem, j, matcher,
3050                            matchedCount = 0,
3051                            i = "0",
3052                            unmatched = seed && [],
3053                            setMatched = [],
3054                            contextBackup = outermostContext,
3055
3056                            // We must always have either seed elements or outermost context
3057                            elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ),
3058
3059                            // Use integer dirruns iff this is the outermost matcher
3060                            dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
3061                            len = elems.length;
3062
3063                        if ( outermost ) {
3064
3065                            // Support: IE 11+, Edge 17 - 18+
3066                            // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
3067                            // two documents; shallow comparisons work.
3068                            // eslint-disable-next-line eqeqeq
3069                            outermostContext = context == document || context || outermost;
3070                        }
3071
3072                        // Add elements passing elementMatchers directly to results
3073                        // Support: IE<9, Safari
3074                        // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
3075                        for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
3076                            if ( byElement && elem ) {
3077                                j = 0;
3078
3079                                // Support: IE 11+, Edge 17 - 18+
3080                                // IE/Edge sometimes throw a "Permission denied" error when strict-comparing
3081                                // two documents; shallow comparisons work.
3082                                // eslint-disable-next-line eqeqeq
3083                                if ( !context && elem.ownerDocument != document ) {
3084                                    setDocument( elem );
3085                                    xml = !documentIsHTML;
3086                                }
3087                                while ( ( matcher = elementMatchers[ j++ ] ) ) {
3088                                    if ( matcher( elem, context || document, xml ) ) {
3089                                        results.push( elem );
3090                                        break;
3091                                    }
3092                                }
3093                                if ( outermost ) {
3094                                    dirruns = dirrunsUnique;
3095                                }
3096                            }
3097
3098                            // Track unmatched elements for set filters
3099                            if ( bySet ) {
3100
3101                                // They will have gone through all possible matchers
3102                                if ( ( elem = !matcher && elem ) ) {
3103                                    matchedCount--;
3104                                }
3105
3106                                // Lengthen the array for every element, matched or not
3107                                if ( seed ) {
3108                                    unmatched.push( elem );
3109                                }
3110                            }
3111                        }
3112
3113                        // `i` is now the count of elements visited above, and adding it to `matchedCount`
3114                        // makes the latter nonnegative.
3115                        matchedCount += i;
3116
3117                        // Apply set filters to unmatched elements
3118                        // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
3119                        // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
3120                        // no element matchers and no seed.
3121                        // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
3122                        // case, which will result in a "00" `matchedCount` that differs from `i` but is also
3123                        // numerically zero.
3124                        if ( bySet && i !== matchedCount ) {
3125                            j = 0;
3126                            while ( ( matcher = setMatchers[ j++ ] ) ) {
3127                                matcher( unmatched, setMatched, context, xml );
3128                            }
3129
3130                            if ( seed ) {
3131
3132                                // Reintegrate element matches to eliminate the need for sorting
3133                                if ( matchedCount > 0 ) {
3134                                    while ( i-- ) {
3135                                        if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
3136                                            setMatched[ i ] = pop.call( results );
3137                                        }
3138                                    }
3139                                }
3140
3141                                // Discard index placeholder values to get only actual matches
3142                                setMatched = condense( setMatched );
3143                            }
3144
3145                            // Add matches to results
3146                            push.apply( results, setMatched );
3147
3148                            // Seedless set matches succeeding multiple successful matchers stipulate sorting
3149                            if ( outermost && !seed && setMatched.length > 0 &&
3150                                ( matchedCount + setMatchers.length ) > 1 ) {
3151
3152                                Sizzle.uniqueSort( results );
3153                            }
3154                        }
3155
3156                        // Override manipulation of globals by nested matchers
3157                        if ( outermost ) {
3158                            dirruns = dirrunsUnique;
3159                            outermostContext = contextBackup;
3160                        }
3161
3162                        return unmatched;
3163                    };
3164
3165                return bySet ?
3166                    markFunction( superMatcher ) :
3167                    superMatcher;
3168            }
3169
3170            compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
3171                var i,
3172                    setMatchers = [],
3173                    elementMatchers = [],
3174                    cached = compilerCache[ selector + " " ];
3175
3176                if ( !cached ) {
3177
3178                    // Generate a function of recursive functions that can be used to check each element
3179                    if ( !match ) {
3180                        match = tokenize( selector );
3181                    }
3182                    i = match.length;
3183                    while ( i-- ) {
3184                        cached = matcherFromTokens( match[ i ] );
3185                        if ( cached[ expando ] ) {
3186                            setMatchers.push( cached );
3187                        } else {
3188                            elementMatchers.push( cached );
3189                        }
3190                    }
3191
3192                    // Cache the compiled function
3193                    cached = compilerCache(
3194                        selector,
3195                        matcherFromGroupMatchers( elementMatchers, setMatchers )
3196                    );
3197
3198                    // Save selector and tokenization
3199                    cached.selector = selector;
3200                }
3201                return cached;
3202            };
3203
3204            /**
3205             * A low-level selection function that works with Sizzle's compiled
3206             *  selector functions
3207             * @param {String|Function} selector A selector or a pre-compiled
3208             *  selector function built with Sizzle.compile
3209             * @param {Element} context
3210             * @param {Array} [results]
3211             * @param {Array} [seed] A set of elements to match against
3212             */
3213            select = Sizzle.select = function( selector, context, results, seed ) {
3214                var i, tokens, token, type, find,
3215                    compiled = typeof selector === "function" && selector,
3216                    match = !seed && tokenize( ( selector = compiled.selector || selector ) );
3217
3218                results = results || [];
3219
3220                // Try to minimize operations if there is only one selector in the list and no seed
3221                // (the latter of which guarantees us context)
3222                if ( match.length === 1 ) {
3223
3224                    // Reduce context if the leading compound selector is an ID
3225                    tokens = match[ 0 ] = match[ 0 ].slice( 0 );
3226                    if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
3227                        context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
3228
3229                        context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
3230                            .replace( runescape, funescape ), context ) || [] )[ 0 ];
3231                        if ( !context ) {
3232                            return results;
3233
3234                            // Precompiled matchers will still verify ancestry, so step up a level
3235                        } else if ( compiled ) {
3236                            context = context.parentNode;
3237                        }
3238
3239                        selector = selector.slice( tokens.shift().value.length );
3240                    }
3241
3242                    // Fetch a seed set for right-to-left matching
3243                    i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;
3244                    while ( i-- ) {
3245                        token = tokens[ i ];
3246
3247                        // Abort if we hit a combinator
3248                        if ( Expr.relative[ ( type = token.type ) ] ) {
3249                            break;
3250                        }
3251                        if ( ( find = Expr.find[ type ] ) ) {
3252
3253                            // Search, expanding context for leading sibling combinators
3254                            if ( ( seed = find(
3255                                token.matches[ 0 ].replace( runescape, funescape ),
3256                                rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
3257                                context
3258                            ) ) ) {
3259
3260                                // If seed is empty or no tokens remain, we can return early
3261                                tokens.splice( i, 1 );
3262                                selector = seed.length && toSelector( tokens );
3263                                if ( !selector ) {
3264                                    push.apply( results, seed );
3265                                    return results;
3266                                }
3267
3268                                break;
3269                            }
3270                        }
3271                    }
3272                }
3273
3274                // Compile and execute a filtering function if one is not provided
3275                // Provide `match` to avoid retokenization if we modified the selector above
3276                ( compiled || compile( selector, match ) )(
3277                    seed,
3278                    context,
3279                    !documentIsHTML,
3280                    results,
3281                    !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
3282                );
3283                return results;
3284            };
3285
3286// One-time assignments
3287
3288// Sort stability
3289            support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
3290
3291// Support: Chrome 14-35+
3292// Always assume duplicates if they aren't passed to the comparison function
3293            support.detectDuplicates = !!hasDuplicate;
3294
3295// Initialize against the default document
3296            setDocument();
3297
3298// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
3299// Detached nodes confoundingly follow *each other*
3300            support.sortDetached = assert( function( el ) {
3301
3302                // Should return 1, but returns 4 (following)
3303                return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
3304            } );
3305
3306// Support: IE<8
3307// Prevent attribute/property "interpolation"
3308// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
3309            if ( !assert( function( el ) {
3310                el.innerHTML = "<a href='#'></a>";
3311                return el.firstChild.getAttribute( "href" ) === "#";
3312            } ) ) {
3313                addHandle( "type|href|height|width", function( elem, name, isXML ) {
3314                    if ( !isXML ) {
3315                        return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
3316                    }
3317                } );
3318            }
3319
3320// Support: IE<9
3321// Use defaultValue in place of getAttribute("value")
3322            if ( !support.attributes || !assert( function( el ) {
3323                el.innerHTML = "<input/>";
3324                el.firstChild.setAttribute( "value", "" );
3325                return el.firstChild.getAttribute( "value" ) === "";
3326            } ) ) {
3327                addHandle( "value", function( elem, _name, isXML ) {
3328                    if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
3329                        return elem.defaultValue;
3330                    }
3331                } );
3332            }
3333
3334// Support: IE<9
3335// Use getAttributeNode to fetch booleans when getAttribute lies
3336            if ( !assert( function( el ) {
3337                return el.getAttribute( "disabled" ) == null;
3338            } ) ) {
3339                addHandle( booleans, function( elem, name, isXML ) {
3340                    var val;
3341                    if ( !isXML ) {
3342                        return elem[ name ] === true ? name.toLowerCase() :
3343                            ( val = elem.getAttributeNode( name ) ) && val.specified ?
3344                                val.value :
3345                                null;
3346                    }
3347                } );
3348            }
3349
3350            return Sizzle;
3351
3352        } )( window );
3353
3354
3355
3356    jQuery.find = Sizzle;
3357    jQuery.expr = Sizzle.selectors;
3358
3359// Deprecated
3360    jQuery.expr[ ":" ] = jQuery.expr.pseudos;
3361    jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
3362    jQuery.text = Sizzle.getText;
3363    jQuery.isXMLDoc = Sizzle.isXML;
3364    jQuery.contains = Sizzle.contains;
3365    jQuery.escapeSelector = Sizzle.escape;
3366
3367
3368
3369
3370    var dir = function( elem, dir, until ) {
3371        var matched = [],
3372            truncate = until !== undefined;
3373
3374        while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
3375            if ( elem.nodeType === 1 ) {
3376                if ( truncate && jQuery( elem ).is( until ) ) {
3377                    break;
3378                }
3379                matched.push( elem );
3380            }
3381        }
3382        return matched;
3383    };
3384
3385
3386    var siblings = function( n, elem ) {
3387        var matched = [];
3388
3389        for ( ; n; n = n.nextSibling ) {
3390            if ( n.nodeType === 1 && n !== elem ) {
3391                matched.push( n );
3392            }
3393        }
3394
3395        return matched;
3396    };
3397
3398
3399    var rneedsContext = jQuery.expr.match.needsContext;
3400
3401
3402
3403    function nodeName( elem, name ) {
3404
3405        return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
3406
3407    }
3408    var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
3409
3410
3411
3412// Implement the identical functionality for filter and not
3413    function winnow( elements, qualifier, not ) {
3414        if ( isFunction( qualifier ) ) {
3415            return jQuery.grep( elements, function( elem, i ) {
3416                return !!qualifier.call( elem, i, elem ) !== not;
3417            } );
3418        }
3419
3420        // Single element
3421        if ( qualifier.nodeType ) {
3422            return jQuery.grep( elements, function( elem ) {
3423                return ( elem === qualifier ) !== not;
3424            } );
3425        }
3426
3427        // Arraylike of elements (jQuery, arguments, Array)
3428        if ( typeof qualifier !== "string" ) {
3429            return jQuery.grep( elements, function( elem ) {
3430                return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
3431            } );
3432        }
3433
3434        // Filtered directly for both simple and complex selectors
3435        return jQuery.filter( qualifier, elements, not );
3436    }
3437
3438    jQuery.filter = function( expr, elems, not ) {
3439        var elem = elems[ 0 ];
3440
3441        if ( not ) {
3442            expr = ":not(" + expr + ")";
3443        }
3444
3445        if ( elems.length === 1 && elem.nodeType === 1 ) {
3446            return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
3447        }
3448
3449        return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
3450            return elem.nodeType === 1;
3451        } ) );
3452    };
3453
3454    jQuery.fn.extend( {
3455        find: function( selector ) {
3456            var i, ret,
3457                len = this.length,
3458                self = this;
3459
3460            if ( typeof selector !== "string" ) {
vendor: 6,677 bytes, lines 3461-3647
3461                return this.pushStack( jQuery( selector ).filter( function() {
3462                    for ( i = 0; i < len; i++ ) {
3463                        if ( jQuery.contains( self[ i ], this ) ) {
3464                            return true;
3465                        }
3466                    }
3467                } ) );
3468            }
3469
3470            ret = this.pushStack( [] );
3471
3472            for ( i = 0; i < len; i++ ) {
3473                jQuery.find( selector, self[ i ], ret );
3474            }
3475
3476            return len > 1 ? jQuery.uniqueSort( ret ) : ret;
3477        },
3478        filter: function( selector ) {
3479            return this.pushStack( winnow( this, selector || [], false ) );
3480        },
3481        not: function( selector ) {
3482            return this.pushStack( winnow( this, selector || [], true ) );
3483        },
3484        is: function( selector ) {
3485            return !!winnow(
3486                this,
3487
3488                // If this is a positional/relative selector, check membership in the returned set
3489                // so $("p:first").is("p:last") won't return true for a doc with two "p".
3490                typeof selector === "string" && rneedsContext.test( selector ) ?
3491                    jQuery( selector ) :
3492                    selector || [],
3493                false
3494            ).length;
3495        }
3496    } );
3497
3498
3499// Initialize a jQuery object
3500
3501
3502// A central reference to the root jQuery(document)
3503    var rootjQuery,
3504
3505        // A simple way to check for HTML strings
3506        // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
3507        // Strict HTML recognition (#11290: must start with <)
3508        // Shortcut simple #id case for speed
3509        rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
3510
3511        init = jQuery.fn.init = function( selector, context, root ) {
3512            var match, elem;
3513
3514            // HANDLE: $(""), $(null), $(undefined), $(false)
3515            if ( !selector ) {
3516                return this;
3517            }
3518
3519            // Method init() accepts an alternate rootjQuery
3520            // so migrate can support jQuery.sub (gh-2101)
3521            root = root || rootjQuery;
3522
3523            // Handle HTML strings
3524            if ( typeof selector === "string" ) {
3525                if ( selector[ 0 ] === "<" &&
3526                    selector[ selector.length - 1 ] === ">" &&
3527                    selector.length >= 3 ) {
3528
3529                    // Assume that strings that start and end with <> are HTML and skip the regex check
3530                    match = [ null, selector, null ];
3531
3532                } else {
3533                    match = rquickExpr.exec( selector );
3534                }
3535
3536                // Match html or make sure no context is specified for #id
3537                if ( match && ( match[ 1 ] || !context ) ) {
3538
3539                    // HANDLE: $(html) -> $(array)
3540                    if ( match[ 1 ] ) {
3541                        context = context instanceof jQuery ? context[ 0 ] : context;
3542
3543                        // Option to run scripts is true for back-compat
3544                        // Intentionally let the error be thrown if parseHTML is not present
3545                        jQuery.merge( this, jQuery.parseHTML(
3546                            match[ 1 ],
3547                            context && context.nodeType ? context.ownerDocument || context : document,
3548                            true
3549                        ) );
3550
3551                        // HANDLE: $(html, props)
3552                        if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
3553                            for ( match in context ) {
3554
3555                                // Properties of context are called as methods if possible
3556                                if ( isFunction( this[ match ] ) ) {
3557                                    this[ match ]( context[ match ] );
3558
3559                                    // ...and otherwise set as attributes
3560                                } else {
3561                                    this.attr( match, context[ match ] );
3562                                }
3563                            }
3564                        }
3565
3566                        return this;
3567
3568                        // HANDLE: $(#id)
3569                    } else {
3570                        elem = document.getElementById( match[ 2 ] );
3571
3572                        if ( elem ) {
3573
3574                            // Inject the element directly into the jQuery object
3575                            this[ 0 ] = elem;
3576                            this.length = 1;
3577                        }
3578                        return this;
3579                    }
3580
3581                    // HANDLE: $(expr, $(...))
3582                } else if ( !context || context.jquery ) {
3583                    return ( context || root ).find( selector );
3584
3585                    // HANDLE: $(expr, context)
3586                    // (which is just equivalent to: $(context).find(expr)
3587                } else {
3588                    return this.constructor( context ).find( selector );
3589                }
3590
3591                // HANDLE: $(DOMElement)
3592            } else if ( selector.nodeType ) {
3593                this[ 0 ] = selector;
3594                this.length = 1;
3595                return this;
3596
3597                // HANDLE: $(function)
3598                // Shortcut for document ready
3599            } else if ( isFunction( selector ) ) {
3600                return root.ready !== undefined ?
3601                    root.ready( selector ) :
3602
3603                    // Execute immediately if ready is not present
3604                    selector( jQuery );
3605            }
3606
3607            return jQuery.makeArray( selector, this );
3608        };
3609
3610// Give the init function the jQuery prototype for later instantiation
3611    init.prototype = jQuery.fn;
3612
3613// Initialize central reference
3614    rootjQuery = jQuery( document );
3615
3616
3617    var rparentsprev = /^(?:parents|prev(?:Until|All))/,
3618
3619        // Methods guaranteed to produce a unique set when starting from a unique set
3620        guaranteedUnique = {
3621            children: true,
3622            contents: true,
3623            next: true,
3624            prev: true
3625        };
3626
3627    jQuery.fn.extend( {
3628        has: function( target ) {
3629            var targets = jQuery( target, this ),
3630                l = targets.length;
3631
3632            return this.filter( function() {
3633                var i = 0;
3634                for ( ; i < l; i++ ) {
3635                    if ( jQuery.contains( this, targets[ i ] ) ) {
3636                        return true;
3637                    }
3638                }
3639            } );
3640        },
3641
3642        closest: function( selectors, context ) {
3643            var cur,
3644                i = 0,
3645                l = this.length,
3646                matched = [],
3647                targets = typeof selectors !== "string" && jQuery( 
vendor: 4,293 bytes, lines 3647-3771
3647selectors );
3648
3649            // Positional selectors never match, since there's no _selection_ context
3650            if ( !rneedsContext.test( selectors ) ) {
3651                for ( ; i < l; i++ ) {
3652                    for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
3653
3654                        // Always skip document fragments
3655                        if ( cur.nodeType < 11 && ( targets ?
3656                            targets.index( cur ) > -1 :
3657
3658                            // Don't pass non-elements to Sizzle
3659                            cur.nodeType === 1 &&
3660                            jQuery.find.matchesSelector( cur, selectors ) ) ) {
3661
3662                            matched.push( cur );
3663                            break;
3664                        }
3665                    }
3666                }
3667            }
3668
3669            return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
3670        },
3671
3672        // Determine the position of an element within the set
3673        index: function( elem ) {
3674
3675            // No argument, return index in parent
3676            if ( !elem ) {
3677                return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
3678            }
3679
3680            // Index in selector
3681            if ( typeof elem === "string" ) {
3682                return indexOf.call( jQuery( elem ), this[ 0 ] );
3683            }
3684
3685            // Locate the position of the desired element
3686            return indexOf.call( this,
3687
3688                // If it receives a jQuery object, the first element is used
3689                elem.jquery ? elem[ 0 ] : elem
3690            );
3691        },
3692
3693        add: function( selector, context ) {
3694            return this.pushStack(
3695                jQuery.uniqueSort(
3696                    jQuery.merge( this.get(), jQuery( selector, context ) )
3697                )
3698            );
3699        },
3700
3701        addBack: function( selector ) {
3702            return this.add( selector == null ?
3703                this.prevObject : this.prevObject.filter( selector )
3704            );
3705        }
3706    } );
3707
3708    function sibling( cur, dir ) {
3709        while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
3710        return cur;
3711    }
3712
3713    jQuery.each( {
3714        parent: function( elem ) {
3715            var parent = elem.parentNode;
3716            return parent && parent.nodeType !== 11 ? parent : null;
3717        },
3718        parents: function( elem ) {
3719            return dir( elem, "parentNode" );
3720        },
3721        parentsUntil: function( elem, _i, until ) {
3722            return dir( elem, "parentNode", until );
3723        },
3724        next: function( elem ) {
3725            return sibling( elem, "nextSibling" );
3726        },
3727        prev: function( elem ) {
3728            return sibling( elem, "previousSibling" );
3729        },
3730        nextAll: function( elem ) {
3731            return dir( elem, "nextSibling" );
3732        },
3733        prevAll: function( elem ) {
3734            return dir( elem, "previousSibling" );
3735        },
3736        nextUntil: function( elem, _i, until ) {
3737            return dir( elem, "nextSibling", until );
3738        },
3739        prevUntil: function( elem, _i, until ) {
3740            return dir( elem, "previousSibling", until );
3741        },
3742        siblings: function( elem ) {
3743            return siblings( ( elem.parentNode || {} ).firstChild, elem );
3744        },
3745        children: function( elem ) {
3746            return siblings( elem.firstChild );
3747        },
3748        contents: function( elem ) {
3749            if ( elem.contentDocument != null &&
3750
3751                // Support: IE 11+
3752                // <object> elements with no `data` attribute has an object
3753                // `contentDocument` with a `null` prototype.
3754                getProto( elem.contentDocument ) ) {
3755
3756                return elem.contentDocument;
3757            }
3758
3759            // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
3760            // Treat the template element as a regular one in browsers that
3761            // don't support it.
3762            if ( nodeName( elem, "template" ) ) {
3763                elem = elem.content || elem;
3764            }
3765
3766            return jQuery.merge( [], elem.childNodes );
3767        }
3768    }, function( name, fn ) {
3769        jQuery.fn[ name ] = function( until, selector ) {
3770            var matched = jQuery.map( this, fn, until );
3771
vendor: 35,404 bytes, lines 3772-4703
3772            if ( name.slice( -5 ) !== "Until" ) {
3773                selector = until;
3774            }
3775
3776            if ( selector && typeof selector === "string" ) {
3777                matched = jQuery.filter( selector, matched );
3778            }
3779
3780            if ( this.length > 1 ) {
3781
3782                // Remove duplicates
3783                if ( !guaranteedUnique[ name ] ) {
3784                    jQuery.uniqueSort( matched );
3785                }
3786
3787                // Reverse order for parents* and prev-derivatives
3788                if ( rparentsprev.test( name ) ) {
3789                    matched.reverse();
3790                }
3791            }
3792
3793            return this.pushStack( matched );
3794        };
3795    } );
3796    var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
3797
3798
3799
3800// Convert String-formatted options into Object-formatted ones
3801    function createOptions( options ) {
3802        var object = {};
3803        jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
3804            object[ flag ] = true;
3805        } );
3806        return object;
3807    }
3808
3809    /*
3810 * Create a callback list using the following parameters:
3811 *
3812 *	options: an optional list of space-separated options that will change how
3813 *			the callback list behaves or a more traditional option object
3814 *
3815 * By default a callback list will act like an event callback list and can be
3816 * "fired" multiple times.
3817 *
3818 * Possible options:
3819 *
3820 *	once:			will ensure the callback list can only be fired once (like a Deferred)
3821 *
3822 *	memory:			will keep track of previous values and will call any callback added
3823 *					after the list has been fired right away with the latest "memorized"
3824 *					values (like a Deferred)
3825 *
3826 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
3827 *
3828 *	stopOnFalse:	interrupt callings when a callback returns false
3829 *
3830 */
3831    jQuery.Callbacks = function( options ) {
3832
3833        // Convert options from String-formatted to Object-formatted if needed
3834        // (we check in cache first)
3835        options = typeof options === "string" ?
3836            createOptions( options ) :
3837            jQuery.extend( {}, options );
3838
3839        var // Flag to know if list is currently firing
3840            firing,
3841
3842            // Last fire value for non-forgettable lists
3843            memory,
3844
3845            // Flag to know if list was already fired
3846            fired,
3847
3848            // Flag to prevent firing
3849            locked,
3850
3851            // Actual callback list
3852            list = [],
3853
3854            // Queue of execution data for repeatable lists
3855            queue = [],
3856
3857            // Index of currently firing callback (modified by add/remove as needed)
3858            firingIndex = -1,
3859
3860            // Fire callbacks
3861            fire = function() {
3862
3863                // Enforce single-firing
3864                locked = locked || options.once;
3865
3866                // Execute callbacks for all pending executions,
3867                // respecting firingIndex overrides and runtime changes
3868                fired = firing = true;
3869                for ( ; queue.length; firingIndex = -1 ) {
3870                    memory = queue.shift();
3871                    while ( ++firingIndex < list.length ) {
3872
3873                        // Run callback and check for early termination
3874                        if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
3875                            options.stopOnFalse ) {
3876
3877                            // Jump to end and forget the data so .add doesn't re-fire
3878                            firingIndex = list.length;
3879                            memory = false;
3880                        }
3881                    }
3882                }
3883
3884                // Forget the data if we're done with it
3885                if ( !options.memory ) {
3886                    memory = false;
3887                }
3888
3889                firing = false;
3890
3891                // Clean up if we're done firing for good
3892                if ( locked ) {
3893
3894                    // Keep an empty list if we have data for future add calls
3895                    if ( memory ) {
3896                        list = [];
3897
3898                        // Otherwise, this object is spent
3899                    } else {
3900                        list = "";
3901                    }
3902                }
3903            },
3904
3905            // Actual Callbacks object
3906            self = {
3907
3908                // Add a callback or a collection of callbacks to the list
3909                add: function() {
3910                    if ( list ) {
3911
3912                        // If we have memory from a past run, we should fire after adding
3913                        if ( memory && !firing ) {
3914                            firingIndex = list.length - 1;
3915                            queue.push( memory );
3916                        }
3917
3918                        ( function add( args ) {
3919                            jQuery.each( args, function( _, arg ) {
3920                                if ( isFunction( arg ) ) {
3921                                    if ( !options.unique || !self.has( arg ) ) {
3922                                        list.push( arg );
3923                                    }
3924                                } else if ( arg && arg.length && toType( arg ) !== "string" ) {
3925
3926                                    // Inspect recursively
3927                                    add( arg );
3928                                }
3929                            } );
3930                        } )( arguments );
3931
3932                        if ( memory && !firing ) {
3933                            fire();
3934                        }
3935                    }
3936                    return this;
3937                },
3938
3939                // Remove a callback from the list
3940                remove: function() {
3941                    jQuery.each( arguments, function( _, arg ) {
3942                        var index;
3943                        while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
3944                            list.splice( index, 1 );
3945
3946                            // Handle firing indexes
3947                            if ( index <= firingIndex ) {
3948                                firingIndex--;
3949                            }
3950                        }
3951                    } );
3952                    return this;
3953                },
3954
3955                // Check if a given callback is in the list.
3956                // If no argument is given, return whether or not list has callbacks attached.
3957                has: function( fn ) {
3958                    return fn ?
3959                        jQuery.inArray( fn, list ) > -1 :
3960                        list.length > 0;
3961                },
3962
3963                // Remove all callbacks from the list
3964                empty: function() {
3965                    if ( list ) {
3966                        list = [];
3967                    }
3968                    return this;
3969                },
3970
3971                // Disable .fire and .add
3972                // Abort any current/pending executions
3973                // Clear all callbacks and values
3974                disable: function() {
3975                    locked = queue = [];
3976                    list = memory = "";
3977                    return this;
3978                },
3979                disabled: function() {
3980                    return !list;
3981                },
3982
3983                // Disable .fire
3984                // Also disable .add unless we have memory (since it would have no effect)
3985                // Abort any pending executions
3986                lock: function() {
3987                    locked = queue = [];
3988                    if ( !memory && !firing ) {
3989                        list = memory = "";
3990                    }
3991                    return this;
3992                },
3993                locked: function() {
3994                    return !!locked;
3995                },
3996
3997                // Call all callbacks with the given context and arguments
3998                fireWith: function( context, args ) {
3999                    if ( !locked ) {
4000                        args = args || [];
4001                        args = [ context, args.slice ? args.slice() : args ];
4002                        queue.push( args );
4003                        if ( !firing ) {
4004                            fire();
4005                        }
4006                    }
4007                    return this;
4008                },
4009
4010                // Call all the callbacks with the given arguments
4011                fire: function() {
4012                    self.fireWith( this, arguments );
4013                    return this;
4014                },
4015
4016                // To know if the callbacks have already been called at least once
4017                fired: function() {
4018                    return !!fired;
4019                }
4020            };
4021
4022        return self;
4023    };
4024
4025
4026    function Identity( v ) {
4027        return v;
4028    }
4029    function Thrower( ex ) {
4030        throw ex;
4031    }
4032
4033    function adoptValue( value, resolve, reject, noValue ) {
4034        var method;
4035
4036        try {
4037
4038            // Check for promise aspect first to privilege synchronous behavior
4039            if ( value && isFunction( ( method = value.promise ) ) ) {
4040                method.call( value ).done( resolve ).fail( reject );
4041
4042                // Other thenables
4043            } else if ( value && isFunction( ( method = value.then ) ) ) {
4044                method.call( value, resolve, reject );
4045
4046                // Other non-thenables
4047            } else {
4048
4049                // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
4050                // * false: [ value ].slice( 0 ) => resolve( value )
4051                // * true: [ value ].slice( 1 ) => resolve()
4052                resolve.apply( undefined, [ value ].slice( noValue ) );
4053            }
4054
4055            // For Promises/A+, convert exceptions into rejections
4056            // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
4057            // Deferred#then to conditionally suppress rejection.
4058        } catch ( value ) {
4059
4060            // Support: Android 4.0 only
4061            // Strict mode functions invoked without .call/.apply get global-object context
4062            reject.apply( undefined, [ value ] );
4063        }
4064    }
4065
4066    jQuery.extend( {
4067
4068        Deferred: function( func ) {
4069            var tuples = [
4070
4071                    // action, add listener, callbacks,
4072                    // ... .then handlers, argument index, [final state]
4073                    [ "notify", "progress", jQuery.Callbacks( "memory" ),
4074                        jQuery.Callbacks( "memory" ), 2 ],
4075                    [ "resolve", "done", jQuery.Callbacks( "once memory" ),
4076                        jQuery.Callbacks( "once memory" ), 0, "resolved" ],
4077                    [ "reject", "fail", jQuery.Callbacks( "once memory" ),
4078                        jQuery.Callbacks( "once memory" ), 1, "rejected" ]
4079                ],
4080                state = "pending",
4081                promise = {
4082                    state: function() {
4083                        return state;
4084                    },
4085                    always: function() {
4086                        deferred.done( arguments ).fail( arguments );
4087                        return this;
4088                    },
4089                    "catch": function( fn ) {
4090                        return promise.then( null, fn );
4091                    },
4092
4093                    // Keep pipe for back-compat
4094                    pipe: function( /* fnDone, fnFail, fnProgress */ ) {
4095                        var fns = arguments;
4096
4097                        return jQuery.Deferred( function( newDefer ) {
4098                            jQuery.each( tuples, function( _i, tuple ) {
4099
4100                                // Map tuples (progress, done, fail) to arguments (done, fail, progress)
4101                                var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
4102
4103                                // deferred.progress(function() { bind to newDefer or newDefer.notify })
4104                                // deferred.done(function() { bind to newDefer or newDefer.resolve })
4105                                // deferred.fail(function() { bind to newDefer or newDefer.reject })
4106                                deferred[ tuple[ 1 ] ]( function() {
4107                                    var returned = fn && fn.apply( this, arguments );
4108                                    if ( returned && isFunction( returned.promise ) ) {
4109                                        returned.promise()
4110                                            .progress( newDefer.notify )
4111                                            .done( newDefer.resolve )
4112                                            .fail( newDefer.reject );
4113                                    } else {
4114                                        newDefer[ tuple[ 0 ] + "With" ](
4115                                            this,
4116                                            fn ? [ returned ] : arguments
4117                                        );
4118                                    }
4119                                } );
4120                            } );
4121                            fns = null;
4122                        } ).promise();
4123                    },
4124                    then: function( onFulfilled, onRejected, onProgress ) {
4125                        var maxDepth = 0;
4126                        function resolve( depth, deferred, handler, special ) {
4127                            return function() {
4128                                var that = this,
4129                                    args = arguments,
4130                                    mightThrow = function() {
4131                                        var returned, then;
4132
4133                                        // Support: Promises/A+ section 2.3.3.3.3
4134                                        // https://promisesaplus.com/#point-59
4135                                        // Ignore double-resolution attempts
4136                                        if ( depth < maxDepth ) {
4137                                            return;
4138                                        }
4139
4140                                        returned = handler.apply( that, args );
4141
4142                                        // Support: Promises/A+ section 2.3.1
4143                                        // https://promisesaplus.com/#point-48
4144                                        if ( returned === deferred.promise() ) {
4145                                            throw new TypeError( "Thenable self-resolution" );
4146                                        }
4147
4148                                        // Support: Promises/A+ sections 2.3.3.1, 3.5
4149                                        // https://promisesaplus.com/#point-54
4150                                        // https://promisesaplus.com/#point-75
4151                                        // Retrieve `then` only once
4152                                        then = returned &&
4153
4154                                            // Support: Promises/A+ section 2.3.4
4155                                            // https://promisesaplus.com/#point-64
4156                                            // Only check objects and functions for thenability
4157                                            ( typeof returned === "object" ||
4158                                                typeof returned === "function" ) &&
4159                                            returned.then;
4160
4161                                        // Handle a returned thenable
4162                                        if ( isFunction( then ) ) {
4163
4164                                            // Special processors (notify) just wait for resolution
4165                                            if ( special ) {
4166                                                then.call(
4167                                                    returned,
4168                                                    resolve( maxDepth, deferred, Identity, special ),
4169                                                    resolve( maxDepth, deferred, Thrower, special )
4170                                                );
4171
4172                                                // Normal processors (resolve) also hook into progress
4173                                            } else {
4174
4175                                                // ...and disregard older resolution values
4176                                                maxDepth++;
4177
4178                                                then.call(
4179                                                    returned,
4180                                                    resolve( maxDepth, deferred, Identity, special ),
4181                                                    resolve( maxDepth, deferred, Thrower, special ),
4182                                                    resolve( maxDepth, deferred, Identity,
4183                                                        deferred.notifyWith )
4184                                                );
4185                                            }
4186
4187                                            // Handle all other returned values
4188                                        } else {
4189
4190                                            // Only substitute handlers pass on context
4191                                            // and multiple values (non-spec behavior)
4192                                            if ( handler !== Identity ) {
4193                                                that = undefined;
4194                                                args = [ returned ];
4195                                            }
4196
4197                                            // Process the value(s)
4198                                            // Default process is resolve
4199                                            ( special || deferred.resolveWith )( that, args );
4200                                        }
4201                                    },
4202
4203                                    // Only normal processors (resolve) catch and reject exceptions
4204                                    process = special ?
4205                                        mightThrow :
4206                                        function() {
4207                                            try {
4208                                                mightThrow();
4209                                            } catch ( e ) {
4210
4211                                                if ( jQuery.Deferred.exceptionHook ) {
4212                                                    jQuery.Deferred.exceptionHook( e,
4213                                                        process.stackTrace );
4214                                                }
4215
4216                                                // Support: Promises/A+ section 2.3.3.3.4.1
4217                                                // https://promisesaplus.com/#point-61
4218                                                // Ignore post-resolution exceptions
4219                                                if ( depth + 1 >= maxDepth ) {
4220
4221                                                    // Only substitute handlers pass on context
4222                                                    // and multiple values (non-spec behavior)
4223                                                    if ( handler !== Thrower ) {
4224                                                        that = undefined;
4225                                                        args = [ e ];
4226                                                    }
4227
4228                                                    deferred.rejectWith( that, args );
4229                                                }
4230                                            }
4231                                        };
4232
4233                                // Support: Promises/A+ section 2.3.3.3.1
4234                                // https://promisesaplus.com/#point-57
4235                                // Re-resolve promises immediately to dodge false rejection from
4236                                // subsequent errors
4237                                if ( depth ) {
4238                                    process();
4239                                } else {
4240
4241                                    // Call an optional hook to record the stack, in case of exception
4242                                    // since it's otherwise lost when execution goes async
4243                                    if ( jQuery.Deferred.getStackHook ) {
4244                                        process.stackTrace = jQuery.Deferred.getStackHook();
4245                                    }
4246                                    window.setTimeout( process );
4247                                }
4248                            };
4249                        }
4250
4251                        return jQuery.Deferred( function( newDefer ) {
4252
4253                            // progress_handlers.add( ... )
4254                            tuples[ 0 ][ 3 ].add(
4255                                resolve(
4256                                    0,
4257                                    newDefer,
4258                                    isFunction( onProgress ) ?
4259                                        onProgress :
4260                                        Identity,
4261                                    newDefer.notifyWith
4262                                )
4263                            );
4264
4265                            // fulfilled_handlers.add( ... )
4266                            tuples[ 1 ][ 3 ].add(
4267                                resolve(
4268                                    0,
4269                                    newDefer,
4270                                    isFunction( onFulfilled ) ?
4271                                        onFulfilled :
4272                                        Identity
4273                                )
4274                            );
4275
4276                            // rejected_handlers.add( ... )
4277                            tuples[ 2 ][ 3 ].add(
4278                                resolve(
4279                                    0,
4280                                    newDefer,
4281                                    isFunction( onRejected ) ?
4282                                        onRejected :
4283                                        Thrower
4284                                )
4285                            );
4286                        } ).promise();
4287                    },
4288
4289                    // Get a promise for this deferred
4290                    // If obj is provided, the promise aspect is added to the object
4291                    promise: function( obj ) {
4292                        return obj != null ? jQuery.extend( obj, promise ) : promise;
4293                    }
4294                },
4295                deferred = {};
4296
4297            // Add list-specific methods
4298            jQuery.each( tuples, function( i, tuple ) {
4299                var list = tuple[ 2 ],
4300                    stateString = tuple[ 5 ];
4301
4302                // promise.progress = list.add
4303                // promise.done = list.add
4304                // promise.fail = list.add
4305                promise[ tuple[ 1 ] ] = list.add;
4306
4307                // Handle state
4308                if ( stateString ) {
4309                    list.add(
4310                        function() {
4311
4312                            // state = "resolved" (i.e., fulfilled)
4313                            // state = "rejected"
4314                            state = stateString;
4315                        },
4316
4317                        // rejected_callbacks.disable
4318                        // fulfilled_callbacks.disable
4319                        tuples[ 3 - i ][ 2 ].disable,
4320
4321                        // rejected_handlers.disable
4322                        // fulfilled_handlers.disable
4323                        tuples[ 3 - i ][ 3 ].disable,
4324
4325                        // progress_callbacks.lock
4326                        tuples[ 0 ][ 2 ].lock,
4327
4328                        // progress_handlers.lock
4329                        tuples[ 0 ][ 3 ].lock
4330                    );
4331                }
4332
4333                // progress_handlers.fire
4334                // fulfilled_handlers.fire
4335                // rejected_handlers.fire
4336                list.add( tuple[ 3 ].fire );
4337
4338                // deferred.notify = function() { deferred.notifyWith(...) }
4339                // deferred.resolve = function() { deferred.resolveWith(...) }
4340                // deferred.reject = function() { deferred.rejectWith(...) }
4341                deferred[ tuple[ 0 ] ] = function() {
4342                    deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
4343                    return this;
4344                };
4345
4346                // deferred.notifyWith = list.fireWith
4347                // deferred.resolveWith = list.fireWith
4348                // deferred.rejectWith = list.fireWith
4349                deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
4350            } );
4351
4352            // Make the deferred a promise
4353            promise.promise( deferred );
4354
4355            // Call given func if any
4356            if ( func ) {
4357                func.call( deferred, deferred );
4358            }
4359
4360            // All done!
4361            return deferred;
4362        },
4363
4364        // Deferred helper
4365        when: function( singleValue ) {
4366            var
4367
4368                // count of uncompleted subordinates
4369                remaining = arguments.length,
4370
4371                // count of unprocessed arguments
4372                i = remaining,
4373
4374                // subordinate fulfillment data
4375                resolveContexts = Array( i ),
4376                resolveValues = slice.call( arguments ),
4377
4378                // the primary Deferred
4379                primary = jQuery.Deferred(),
4380
4381                // subordinate callback factory
4382                updateFunc = function( i ) {
4383                    return function( value ) {
4384                        resolveContexts[ i ] = this;
4385                        resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
4386                        if ( !( --remaining ) ) {
4387                            primary.resolveWith( resolveContexts, resolveValues );
4388                        }
4389                    };
4390                };
4391
4392            // Single- and empty arguments are adopted like Promise.resolve
4393            if ( remaining <= 1 ) {
4394                adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
4395                    !remaining );
4396
4397                // Use .then() to unwrap secondary thenables (cf. gh-3000)
4398                if ( primary.state() === "pending" ||
4399                    isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
4400
4401                    return primary.then();
4402                }
4403            }
4404
4405            // Multiple arguments are aggregated like Promise.all array elements
4406            while ( i-- ) {
4407                adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
4408            }
4409
4410            return primary.promise();
4411        }
4412    } );
4413
4414
4415// These usually indicate a programmer mistake during development,
4416// warn about them ASAP rather than swallowing them by default.
4417    var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
4418
4419    jQuery.Deferred.exceptionHook = function( error, stack ) {
4420
4421        // Support: IE 8 - 9 only
4422        // Console exists when dev tools are open, which can happen at any time
4423        if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
4424            window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
4425        }
4426    };
4427
4428
4429
4430
4431    jQuery.readyException = function( error ) {
4432        window.setTimeout( function() {
4433            throw error;
4434        } );
4435    };
4436
4437
4438
4439
4440// The deferred used on DOM ready
4441    var readyList = jQuery.Deferred();
4442
4443    jQuery.fn.ready = function( fn ) {
4444
4445        readyList
4446            .then( fn )
4447
4448            // Wrap jQuery.readyException in a function so that the lookup
4449            // happens at the time of error handling instead of callback
4450            // registration.
4451            .catch( function( error ) {
4452                jQuery.readyException( error );
4453            } );
4454
4455        return this;
4456    };
4457
4458    jQuery.extend( {
4459
4460        // Is the DOM ready to be used? Set to true once it occurs.
4461        isReady: false,
4462
4463        // A counter to track how many items to wait for before
4464        // the ready event fires. See #6781
4465        readyWait: 1,
4466
4467        // Handle when the DOM is ready
4468        ready: function( wait ) {
4469
4470            // Abort if there are pending holds or we're already ready
4471            if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
4472                return;
4473            }
4474
4475            // Remember that the DOM is ready
4476            jQuery.isReady = true;
4477
4478            // If a normal DOM Ready event fired, decrement, and wait if need be
4479            if ( wait !== true && --jQuery.readyWait > 0 ) {
4480                return;
4481            }
4482
4483            // If there are functions bound, to execute
4484            readyList.resolveWith( document, [ jQuery ] );
4485        }
4486    } );
4487
4488    jQuery.ready.then = readyList.then;
4489
4490// The ready event handler and self cleanup method
4491    function completed() {
4492        document.removeEventListener( "DOMContentLoaded", completed );
4493        window.removeEventListener( "load", completed );
4494        jQuery.ready();
4495    }
4496
4497// Catch cases where $(document).ready() is called
4498// after the browser event has already occurred.
4499// Support: IE <=9 - 10 only
4500// Older IE sometimes signals "interactive" too soon
4501    if ( document.readyState === "complete" ||
4502        ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
4503
4504        // Handle it asynchronously to allow scripts the opportunity to delay ready
4505        window.setTimeout( jQuery.ready );
4506
4507    } else {
4508
4509        // Use the handy event callback
4510        document.addEventListener( "DOMContentLoaded", completed );
4511
4512        // A fallback to window.onload, that will always work
4513        window.addEventListener( "load", completed );
4514    }
4515
4516
4517
4518
4519// Multifunctional method to get and set values of a collection
4520// The value/s can optionally be executed if it's a function
4521    var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
4522        var i = 0,
4523            len = elems.length,
4524            bulk = key == null;
4525
4526        // Sets many values
4527        if ( toType( key ) === "object" ) {
4528            chainable = true;
4529            for ( i in key ) {
4530                access( elems, fn, i, key[ i ], true, emptyGet, raw );
4531            }
4532
4533            // Sets one value
4534        } else if ( value !== undefined ) {
4535            chainable = true;
4536
4537            if ( !isFunction( value ) ) {
4538                raw = true;
4539            }
4540
4541            if ( bulk ) {
4542
4543                // Bulk operations run against the entire set
4544                if ( raw ) {
4545                    fn.call( elems, value );
4546                    fn = null;
4547
4548                    // ...except when executing function values
4549                } else {
4550                    bulk = fn;
4551                    fn = function( elem, _key, value ) {
4552                        return bulk.call( jQuery( elem ), value );
4553                    };
4554                }
4555            }
4556
4557            if ( fn ) {
4558                for ( ; i < len; i++ ) {
4559                    fn(
4560                        elems[ i ], key, raw ?
4561                            value :
4562                            value.call( elems[ i ], i, fn( elems[ i ], key ) )
4563                    );
4564                }
4565            }
4566        }
4567
4568        if ( chainable ) {
4569            return elems;
4570        }
4571
4572        // Gets
4573        if ( bulk ) {
4574            return fn.call( elems );
4575        }
4576
4577        return len ? fn( elems[ 0 ], key ) : emptyGet;
4578    };
4579
4580
4581// Matches dashed string for camelizing
4582    var rmsPrefix = /^-ms-/,
4583        rdashAlpha = /-([a-z])/g;
4584
4585// Used by camelCase as callback to replace()
4586    function fcamelCase( _all, letter ) {
4587        return letter.toUpperCase();
4588    }
4589
4590// Convert dashed to camelCase; used by the css and data modules
4591// Support: IE <=9 - 11, Edge 12 - 15
4592// Microsoft forgot to hump their vendor prefix (#9572)
4593    function camelCase( string ) {
4594        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
4595    }
4596    var acceptData = function( owner ) {
4597
4598        // Accepts only:
4599        //  - Node
4600        //    - Node.ELEMENT_NODE
4601        //    - Node.DOCUMENT_NODE
4602        //  - Object
4603        //    - Any
4604        return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
4605    };
4606
4607
4608
4609
4610    function Data() {
4611        this.expando = jQuery.expando + Data.uid++;
4612    }
4613
4614    Data.uid = 1;
4615
4616    Data.prototype = {
4617
4618        cache: function( owner ) {
4619
4620            // Check if the owner object already has a cache
4621            var value = owner[ this.expando ];
4622
4623            // If not, create one
4624            if ( !value ) {
4625                value = {};
4626
4627                // We can accept data for non-element nodes in modern browsers,
4628                // but we should not, see #8335.
4629                // Always return an empty object.
4630                if ( acceptData( owner ) ) {
4631
4632                    // If it is a node unlikely to be stringify-ed or looped over
4633                    // use plain assignment
4634                    if ( owner.nodeType ) {
4635                        owner[ this.expando ] = value;
4636
4637                        // Otherwise secure it in a non-enumerable property
4638                        // configurable must be true to allow the property to be
4639                        // deleted when data is removed
4640                    } else {
4641                        Object.defineProperty( owner, this.expando, {
4642                            value: value,
4643                            configurable: true
4644                        } );
4645                    }
4646                }
4647            }
4648
4649            return value;
4650        },
4651        set: function( owner, data, value ) {
4652            var prop,
4653                cache = this.cache( owner );
4654
4655            // Handle: [ owner, key, value ] args
4656            // Always use camelCase key (gh-2257)
4657            if ( typeof data === "string" ) {
4658                cache[ camelCase( data ) ] = value;
4659
4660                // Handle: [ owner, { properties } ] args
4661            } else {
4662
4663                // Copy the properties one-by-one to the cache object
4664                for ( prop in data ) {
4665                    cache[ camelCase( prop ) ] = data[ prop ];
4666                }
4667            }
4668            return cache;
4669        },
4670        get: function( owner, key ) {
4671            return key === undefined ?
4672                this.cache( owner ) :
4673
4674                // Always use camelCase key (gh-2257)
4675                owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
4676        },
4677        access: function( owner, key, value ) {
4678
4679            // In cases where either:
4680            //
4681            //   1. No key was specified
4682            //   2. A string key was specified, but no value provided
4683            //
4684            // Take the "read" path and allow the get method to determine
4685            // which value to return, respectively either:
4686            //
4687            //   1. The entire cache object
4688            //   2. The data stored at the key
4689            //
4690            if ( key === undefined ||
4691                ( ( key && typeof key === "string" ) && value === undefined ) ) {
4692
4693                return this.get( owner, key );
4694            }
4695
4696            // When the key is not a string, or both a key and value
4697            // are specified, set or extend (existing objects) with either:
4698            //
4699            //   1. An object of properties
4700            //   2. A key and value
4701            //
4702            this.set( owner, key, value );
4703
vendor: 9,776 bytes, lines 4704-4994
4704            // Since the "set" path can have two possible entry points
4705            // return the expected data based on which path was taken[*]
4706            return value !== undefined ? value : key;
4707        },
4708        remove: function( owner, key ) {
4709            var i,
4710                cache = owner[ this.expando ];
4711
4712            if ( cache === undefined ) {
4713                return;
4714            }
4715
4716            if ( key !== undefined ) {
4717
4718                // Support array or space separated string of keys
4719                if ( Array.isArray( key ) ) {
4720
4721                    // If key is an array of keys...
4722                    // We always set camelCase keys, so remove that.
4723                    key = key.map( camelCase );
4724                } else {
4725                    key = camelCase( key );
4726
4727                    // If a key with the spaces exists, use it.
4728                    // Otherwise, create an array by matching non-whitespace
4729                    key = key in cache ?
4730                        [ key ] :
4731                        ( key.match( rnothtmlwhite ) || [] );
4732                }
4733
4734                i = key.length;
4735
4736                while ( i-- ) {
4737                    delete cache[ key[ i ] ];
4738                }
4739            }
4740
4741            // Remove the expando if there's no more data
4742            if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
4743
4744                // Support: Chrome <=35 - 45
4745                // Webkit & Blink performance suffers when deleting properties
4746                // from DOM nodes, so set to undefined instead
4747                // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
4748                if ( owner.nodeType ) {
4749                    owner[ this.expando ] = undefined;
4750                } else {
4751                    delete owner[ this.expando ];
4752                }
4753            }
4754        },
4755        hasData: function( owner ) {
4756            var cache = owner[ this.expando ];
4757            return cache !== undefined && !jQuery.isEmptyObject( cache );
4758        }
4759    };
4760    var dataPriv = new Data();
4761
4762    var dataUser = new Data();
4763
4764
4765
4766//	Implementation Summary
4767//
4768//	1. Enforce API surface and semantic compatibility with 1.9.x branch
4769//	2. Improve the module's maintainability by reducing the storage
4770//		paths to a single mechanism.
4771//	3. Use the same single mechanism to support "private" and "user" data.
4772//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
4773//	5. Avoid exposing implementation details on user objects (eg. expando properties)
4774//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
4775
4776    var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
4777        rmultiDash = /[A-Z]/g;
4778
4779    function getData( data ) {
4780        if ( data === "true" ) {
4781            return true;
4782        }
4783
4784        if ( data === "false" ) {
4785            return false;
4786        }
4787
4788        if ( data === "null" ) {
4789            return null;
4790        }
4791
4792        // Only convert to a number if it doesn't change the string
4793        if ( data === +data + "" ) {
4794            return +data;
4795        }
4796
4797        if ( rbrace.test( data ) ) {
4798            return JSON.parse( data );
4799        }
4800
4801        return data;
4802    }
4803
4804    function dataAttr( elem, key, data ) {
4805        var name;
4806
4807        // If nothing was found internally, try to fetch any
4808        // data from the HTML5 data-* attribute
4809        if ( data === undefined && elem.nodeType === 1 ) {
4810            name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
4811            data = elem.getAttribute( name );
4812
4813            if ( typeof data === "string" ) {
4814                try {
4815                    data = getData( data );
4816                } catch ( e ) {}
4817
4818                // Make sure we set the data so it isn't changed later
4819                dataUser.set( elem, key, data );
4820            } else {
4821                data = undefined;
4822            }
4823        }
4824        return data;
4825    }
4826
4827    jQuery.extend( {
4828        hasData: function( elem ) {
4829            return dataUser.hasData( elem ) || dataPriv.hasData( elem );
4830        },
4831
4832        data: function( elem, name, data ) {
4833            return dataUser.access( elem, name, data );
4834        },
4835
4836        removeData: function( elem, name ) {
4837            dataUser.remove( elem, name );
4838        },
4839
4840        // TODO: Now that all calls to _data and _removeData have been replaced
4841        // with direct calls to dataPriv methods, these can be deprecated.
4842        _data: function( elem, name, data ) {
4843            return dataPriv.access( elem, name, data );
4844        },
4845
4846        _removeData: function( elem, name ) {
4847            dataPriv.remove( elem, name );
4848        }
4849    } );
4850
4851    jQuery.fn.extend( {
4852        data: function( key, value ) {
4853            var i, name, data,
4854                elem = this[ 0 ],
4855                attrs = elem && elem.attributes;
4856
4857            // Gets all values
4858            if ( key === undefined ) {
4859                if ( this.length ) {
4860                    data = dataUser.get( elem );
4861
4862                    if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
4863                        i = attrs.length;
4864                        while ( i-- ) {
4865
4866                            // Support: IE 11 only
4867                            // The attrs elements can be null (#14894)
4868                            if ( attrs[ i ] ) {
4869                                name = attrs[ i ].name;
4870                                if ( name.indexOf( "data-" ) === 0 ) {
4871                                    name = camelCase( name.slice( 5 ) );
4872                                    dataAttr( elem, name, data[ name ] );
4873                                }
4874                            }
4875                        }
4876                        dataPriv.set( elem, "hasDataAttrs", true );
4877                    }
4878                }
4879
4880                return data;
4881            }
4882
4883            // Sets multiple values
4884            if ( typeof key === "object" ) {
4885                return this.each( function() {
4886                    dataUser.set( this, key );
4887                } );
4888            }
4889
4890            return access( this, function( value ) {
4891                var data;
4892
4893                // The calling jQuery object (element matches) is not empty
4894                // (and therefore has an element appears at this[ 0 ]) and the
4895                // `value` parameter was not undefined. An empty jQuery object
4896                // will result in `undefined` for elem = this[ 0 ] which will
4897                // throw an exception if an attempt to read a data cache is made.
4898                if ( elem && value === undefined ) {
4899
4900                    // Attempt to get data from the cache
4901                    // The key will always be camelCased in Data
4902                    data = dataUser.get( elem, key );
4903                    if ( data !== undefined ) {
4904                        return data;
4905                    }
4906
4907                    // Attempt to "discover" the data in
4908                    // HTML5 custom data-* attrs
4909                    data = dataAttr( elem, key );
4910                    if ( data !== undefined ) {
4911                        return data;
4912                    }
4913
4914                    // We tried really hard, but the data doesn't exist.
4915                    return;
4916                }
4917
4918                // Set the data...
4919                this.each( function() {
4920
4921                    // We always store the camelCased key
4922                    dataUser.set( this, key, value );
4923                } );
4924            }, null, value, arguments.length > 1, null, true );
4925        },
4926
4927        removeData: function( key ) {
4928            return this.each( function() {
4929                dataUser.remove( this, key );
4930            } );
4931        }
4932    } );
4933
4934
4935    jQuery.extend( {
4936        queue: function( elem, type, data ) {
4937            var queue;
4938
4939            if ( elem ) {
4940                type = ( type || "fx" ) + "queue";
4941                queue = dataPriv.get( elem, type );
4942
4943                // Speed up dequeue by getting out quickly if this is just a lookup
4944                if ( data ) {
4945                    if ( !queue || Array.isArray( data ) ) {
4946                        queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
4947                    } else {
4948                        queue.push( data );
4949                    }
4950                }
4951                return queue || [];
4952            }
4953        },
4954
4955        dequeue: function( elem, type ) {
4956            type = type || "fx";
4957
4958            var queue = jQuery.queue( elem, type ),
4959                startLength = queue.length,
4960                fn = queue.shift(),
4961                hooks = jQuery._queueHooks( elem, type ),
4962                next = function() {
4963                    jQuery.dequeue( elem, type );
4964                };
4965
4966            // If the fx queue is dequeued, always remove the progress sentinel
4967            if ( fn === "inprogress" ) {
4968                fn = queue.shift();
4969                startLength--;
4970            }
4971
4972            if ( fn ) {
4973
4974                // Add a progress sentinel to prevent the fx queue from being
4975                // automatically dequeued
4976                if ( type === "fx" ) {
4977                    queue.unshift( "inprogress" );
4978                }
4979
4980                // Clear up the last queue stop function
4981                delete hooks.stop;
4982                fn.call( elem, next, hooks );
4983            }
4984
4985            if ( !startLength && hooks ) {
4986                hooks.empty.fire();
4987            }
4988        },
4989
4990        // Not public - generate a queueHooks object, or return the current one
4991        _queueHooks: function( elem, type ) {
4992            var key = type + "queueHooks";
4993            return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
4994                empty: jQuery.Callbacks( "once memory" ).add( function() {
vendor: 14,054 bytes, lines 4995-5417
4995                    dataPriv.remove( elem, [ type + "queue", key ] );
4996                } )
4997            } );
4998        }
4999    } );
5000
5001    jQuery.fn.extend( {
5002        queue: function( type, data ) {
5003            var setter = 2;
5004
5005            if ( typeof type !== "string" ) {
5006                data = type;
5007                type = "fx";
5008                setter--;
5009            }
5010
5011            if ( arguments.length < setter ) {
5012                return jQuery.queue( this[ 0 ], type );
5013            }
5014
5015            return data === undefined ?
5016                this :
5017                this.each( function() {
5018                    var queue = jQuery.queue( this, type, data );
5019
5020                    // Ensure a hooks for this queue
5021                    jQuery._queueHooks( this, type );
5022
5023                    if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
5024                        jQuery.dequeue( this, type );
5025                    }
5026                } );
5027        },
5028        dequeue: function( type ) {
5029            return this.each( function() {
5030                jQuery.dequeue( this, type );
5031            } );
5032        },
5033        clearQueue: function( type ) {
5034            return this.queue( type || "fx", [] );
5035        },
5036
5037        // Get a promise resolved when queues of a certain type
5038        // are emptied (fx is the type by default)
5039        promise: function( type, obj ) {
5040            var tmp,
5041                count = 1,
5042                defer = jQuery.Deferred(),
5043                elements = this,
5044                i = this.length,
5045                resolve = function() {
5046                    if ( !( --count ) ) {
5047                        defer.resolveWith( elements, [ elements ] );
5048                    }
5049                };
5050
5051            if ( typeof type !== "string" ) {
5052                obj = type;
5053                type = undefined;
5054            }
5055            type = type || "fx";
5056
5057            while ( i-- ) {
5058                tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
5059                if ( tmp && tmp.empty ) {
5060                    count++;
5061                    tmp.empty.add( resolve );
5062                }
5063            }
5064            resolve();
5065            return defer.promise( obj );
5066        }
5067    } );
5068    var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
5069
5070    var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
5071
5072
5073    var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
5074
5075    var documentElement = document.documentElement;
5076
5077
5078
5079    var isAttached = function( elem ) {
5080            return jQuery.contains( elem.ownerDocument, elem );
5081        },
5082        composed = { composed: true };
5083
5084    // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
5085    // Check attachment across shadow DOM boundaries when possible (gh-3504)
5086    // Support: iOS 10.0-10.2 only
5087    // Early iOS 10 versions support `attachShadow` but not `getRootNode`,
5088    // leading to errors. We need to check for `getRootNode`.
5089    if ( documentElement.getRootNode ) {
5090        isAttached = function( elem ) {
5091            return jQuery.contains( elem.ownerDocument, elem ) ||
5092                elem.getRootNode( composed ) === elem.ownerDocument;
5093        };
5094    }
5095    var isHiddenWithinTree = function( elem, el ) {
5096
5097        // isHiddenWithinTree might be called from jQuery#filter function;
5098        // in that case, element will be second argument
5099        elem = el || elem;
5100
5101        // Inline style trumps all
5102        return elem.style.display === "none" ||
5103            elem.style.display === "" &&
5104
5105            // Otherwise, check computed style
5106            // Support: Firefox <=43 - 45
5107            // Disconnected elements can have computed display: none, so first confirm that elem is
5108            // in the document.
5109            isAttached( elem ) &&
5110
5111            jQuery.css( elem, "display" ) === "none";
5112    };
5113
5114
5115
5116    function adjustCSS( elem, prop, valueParts, tween ) {
5117        var adjusted, scale,
5118            maxIterations = 20,
5119            currentValue = tween ?
5120                function() {
5121                    return tween.cur();
5122                } :
5123                function() {
5124                    return jQuery.css( elem, prop, "" );
5125                },
5126            initial = currentValue(),
5127            unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
5128
5129            // Starting value computation is required for potential unit mismatches
5130            initialInUnit = elem.nodeType &&
5131                ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
5132                rcssNum.exec( jQuery.css( elem, prop ) );
5133
5134        if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
5135
5136            // Support: Firefox <=54
5137            // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
5138            initial = initial / 2;
5139
5140            // Trust units reported by jQuery.css
5141            unit = unit || initialInUnit[ 3 ];
5142
5143            // Iteratively approximate from a nonzero starting point
5144            initialInUnit = +initial || 1;
5145
5146            while ( maxIterations-- ) {
5147
5148                // Evaluate and update our best guess (doubling guesses that zero out).
5149                // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
5150                jQuery.style( elem, prop, initialInUnit + unit );
5151                if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
5152                    maxIterations = 0;
5153                }
5154                initialInUnit = initialInUnit / scale;
5155
5156            }
5157
5158            initialInUnit = initialInUnit * 2;
5159            jQuery.style( elem, prop, initialInUnit + unit );
5160
5161            // Make sure we update the tween properties later on
5162            valueParts = valueParts || [];
5163        }
5164
5165        if ( valueParts ) {
5166            initialInUnit = +initialInUnit || +initial || 0;
5167
5168            // Apply relative offset (+=/-=) if specified
5169            adjusted = valueParts[ 1 ] ?
5170                initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
5171                +valueParts[ 2 ];
5172            if ( tween ) {
5173                tween.unit = unit;
5174                tween.start = initialInUnit;
5175                tween.end = adjusted;
5176            }
5177        }
5178        return adjusted;
5179    }
5180
5181
5182    var defaultDisplayMap = {};
5183
5184    function getDefaultDisplay( elem ) {
5185        var temp,
5186            doc = elem.ownerDocument,
5187            nodeName = elem.nodeName,
5188            display = defaultDisplayMap[ nodeName ];
5189
5190        if ( display ) {
5191            return display;
5192        }
5193
5194        temp = doc.body.appendChild( doc.createElement( nodeName ) );
5195        display = jQuery.css( temp, "display" );
5196
5197        temp.parentNode.removeChild( temp );
5198
5199        if ( display === "none" ) {
5200            display = "block";
5201        }
5202        defaultDisplayMap[ nodeName ] = display;
5203
5204        return display;
5205    }
5206
5207    function showHide( elements, show ) {
5208        var display, elem,
5209            values = [],
5210            index = 0,
5211            length = elements.length;
5212
5213        // Determine new display value for elements that need to change
5214        for ( ; index < length; index++ ) {
5215            elem = elements[ index ];
5216            if ( !elem.style ) {
5217                continue;
5218            }
5219
5220            display = elem.style.display;
5221            if ( show ) {
5222
5223                // Since we force visibility upon cascade-hidden elements, an immediate (and slow)
5224                // check is required in this first loop unless we have a nonempty display value (either
5225                // inline or about-to-be-restored)
5226                if ( display === "none" ) {
5227                    values[ index ] = dataPriv.get( elem, "display" ) || null;
5228                    if ( !values[ index ] ) {
5229                        elem.style.display = "";
5230                    }
5231                }
5232                if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
5233                    values[ index ] = getDefaultDisplay( elem );
5234                }
5235            } else {
5236                if ( display !== "none" ) {
5237                    values[ index ] = "none";
5238
5239                    // Remember what we're overwriting
5240                    dataPriv.set( elem, "display", display );
5241                }
5242            }
5243        }
5244
5245        // Set the display of the elements in a second loop to avoid constant reflow
5246        for ( index = 0; index < length; index++ ) {
5247            if ( values[ index ] != null ) {
5248                elements[ index ].style.display = values[ index ];
5249            }
5250        }
5251
5252        return elements;
5253    }
5254
5255    jQuery.fn.extend( {
5256        show: function() {
5257            return showHide( this, true );
5258        },
5259        hide: function() {
5260            return showHide( this );
5261        },
5262        toggle: function( state ) {
5263            if ( typeof state === "boolean" ) {
5264                return state ? this.show() : this.hide();
5265            }
5266
5267            return this.each( function() {
5268                if ( isHiddenWithinTree( this ) ) {
5269                    jQuery( this ).show();
5270                } else {
5271                    jQuery( this ).hide();
5272                }
5273            } );
5274        }
5275    } );
5276    var rcheckableType = ( /^(?:checkbox|radio)$/i );
5277
5278    var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
5279
5280    var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
5281
5282
5283
5284    ( function() {
5285        var fragment = document.createDocumentFragment(),
5286            div = fragment.appendChild( document.createElement( "div" ) ),
5287            input = document.createElement( "input" );
5288
5289        // Support: Android 4.0 - 4.3 only
5290        // Check state lost if the name is set (#11217)
5291        // Support: Windows Web Apps (WWA)
5292        // `name` and `type` must use .setAttribute for WWA (#14901)
5293        input.setAttribute( "type", "radio" );
5294        input.setAttribute( "checked", "checked" );
5295        input.setAttribute( "name", "t" );
5296
5297        div.appendChild( input );
5298
5299        // Support: Android <=4.1 only
5300        // Older WebKit doesn't clone checked state correctly in fragments
5301        support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
5302
5303        // Support: IE <=11 only
5304        // Make sure textarea (and checkbox) defaultValue is properly cloned
5305        div.innerHTML = "<textarea>x</textarea>";
5306        support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
5307
5308        // Support: IE <=9 only
5309        // IE <=9 replaces <option> tags with their contents when inserted outside of
5310        // the select element.
5311        div.innerHTML = "<option></option>";
5312        support.option = !!div.lastChild;
5313    } )();
5314
5315
5316// We have to close these tags to support XHTML (#13200)
5317    var wrapMap = {
5318
5319        // XHTML parsers do not magically insert elements in the
5320        // same way that tag soup parsers do. So we cannot shorten
5321        // this by omitting <tbody> or other required elements.
5322        thead: [ 1, "<table>", "</table>" ],
5323        col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
5324        tr: [ 2, "<table><tbody>", "</tbody></table>" ],
5325        td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
5326
5327        _default: [ 0, "", "" ]
5328    };
5329
5330    wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5331    wrapMap.th = wrapMap.td;
5332
5333// Support: IE <=9 only
5334    if ( !support.option ) {
5335        wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
5336    }
5337
5338
5339    function getAll( context, tag ) {
5340
5341        // Support: IE <=9 - 11 only
5342        // Use typeof to avoid zero-argument method invocation on host objects (#15151)
5343        var ret;
5344
5345        if ( typeof context.getElementsByTagName !== "undefined" ) {
5346            ret = context.getElementsByTagName( tag || "*" );
5347
5348        } else if ( typeof context.querySelectorAll !== "undefined" ) {
5349            ret = context.querySelectorAll( tag || "*" );
5350
5351        } else {
5352            ret = [];
5353        }
5354
5355        if ( tag === undefined || tag && nodeName( context, tag ) ) {
5356            return jQuery.merge( [ context ], ret );
5357        }
5358
5359        return ret;
5360    }
5361
5362
5363// Mark scripts as having already been evaluated
5364    function setGlobalEval( elems, refElements ) {
5365        var i = 0,
5366            l = elems.length;
5367
5368        for ( ; i < l; i++ ) {
5369            dataPriv.set(
5370                elems[ i ],
5371                "globalEval",
5372                !refElements || dataPriv.get( refElements[ i ], "globalEval" )
5373            );
5374        }
5375    }
5376
5377
5378    var rhtml = /<|&#?\w+;/;
5379
5380    function buildFragment( elems, context, scripts, selection, ignored ) {
5381        var elem, tmp, tag, wrap, attached, j,
5382            fragment = context.createDocumentFragment(),
5383            nodes = [],
5384            i = 0,
5385            l = elems.length;
5386
5387        for ( ; i < l; i++ ) {
5388            elem = elems[ i ];
5389
5390            if ( elem || elem === 0 ) {
5391
5392                // Add nodes directly
5393                if ( toType( elem ) === "object" ) {
5394
5395                    // Support: Android <=4.0 only, PhantomJS 1 only
5396                    // push.apply(_, arraylike) throws on ancient WebKit
5397                    jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
5398
5399                    // Convert non-html into a text node
5400                } else if ( !rhtml.test( elem ) ) {
5401                    nodes.push( context.createTextNode( elem ) );
5402
5403                    // Convert html into DOM nodes
5404                } else {
5405                    tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
5406
5407                    // Deserialize a standard representation
5408                    tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
5409                    wrap = wrapMap[ tag ] || wrapMap._default;
5410                    tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
5411
5412                    // Descend through wrappers to the right content
5413                    j = wrap[ 0 ];
5414                    while ( j-- ) {
5415                        tmp = tmp.lastChild;
5416                    }
5417
vendor: 9,502 bytes, lines 5418-5693
5418                    // Support: Android <=4.0 only, PhantomJS 1 only
5419                    // push.apply(_, arraylike) throws on ancient WebKit
5420                    jQuery.merge( nodes, tmp.childNodes );
5421
5422                    // Remember the top-level container
5423                    tmp = fragment.firstChild;
5424
5425                    // Ensure the created nodes are orphaned (#12392)
5426                    tmp.textContent = "";
5427                }
5428            }
5429        }
5430
5431        // Remove wrapper from fragment
5432        fragment.textContent = "";
5433
5434        i = 0;
5435        while ( ( elem = nodes[ i++ ] ) ) {
5436
5437            // Skip elements already in the context collection (trac-4087)
5438            if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
5439                if ( ignored ) {
5440                    ignored.push( elem );
5441                }
5442                continue;
5443            }
5444
5445            attached = isAttached( elem );
5446
5447            // Append to fragment
5448            tmp = getAll( fragment.appendChild( elem ), "script" );
5449
5450            // Preserve script evaluation history
5451            if ( attached ) {
5452                setGlobalEval( tmp );
5453            }
5454
5455            // Capture executables
5456            if ( scripts ) {
5457                j = 0;
5458                while ( ( elem = tmp[ j++ ] ) ) {
5459                    if ( rscriptType.test( elem.type || "" ) ) {
5460                        scripts.push( elem );
5461                    }
5462                }
5463            }
5464        }
5465
5466        return fragment;
5467    }
5468
5469
5470    var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
5471
5472    function returnTrue() {
5473        return true;
5474    }
5475
5476    function returnFalse() {
5477        return false;
5478    }
5479
5480// Support: IE <=9 - 11+
5481// focus() and blur() are asynchronous, except when they are no-op.
5482// So expect focus to be synchronous when the element is already active,
5483// and blur to be synchronous when the element is not already active.
5484// (focus and blur are always synchronous in other supported browsers,
5485// this just defines when we can count on it).
5486    function expectSync( elem, type ) {
5487        return ( elem === safeActiveElement() ) === ( type === "focus" );
5488    }
5489
5490// Support: IE <=9 only
5491// Accessing document.activeElement can throw unexpectedly
5492// https://bugs.jquery.com/ticket/13393
5493    function safeActiveElement() {
5494        try {
5495            return document.activeElement;
5496        } catch ( err ) { }
5497    }
5498
5499    function on( elem, types, selector, data, fn, one ) {
5500        var origFn, type;
5501
5502        // Types can be a map of types/handlers
5503        if ( typeof types === "object" ) {
5504
5505            // ( types-Object, selector, data )
5506            if ( typeof selector !== "string" ) {
5507
5508                // ( types-Object, data )
5509                data = data || selector;
5510                selector = undefined;
5511            }
5512            for ( type in types ) {
5513                on( elem, type, selector, data, types[ type ], one );
5514            }
5515            return elem;
5516        }
5517
5518        if ( data == null && fn == null ) {
5519
5520            // ( types, fn )
5521            fn = selector;
5522            data = selector = undefined;
5523        } else if ( fn == null ) {
5524            if ( typeof selector === "string" ) {
5525
5526                // ( types, selector, fn )
5527                fn = data;
5528                data = undefined;
5529            } else {
5530
5531                // ( types, data, fn )
5532                fn = data;
5533                data = selector;
5534                selector = undefined;
5535            }
5536        }
5537        if ( fn === false ) {
5538            fn = returnFalse;
5539        } else if ( !fn ) {
5540            return elem;
5541        }
5542
5543        if ( one === 1 ) {
5544            origFn = fn;
5545            fn = function( event ) {
5546
5547                // Can use an empty set, since event contains the info
5548                jQuery().off( event );
5549                return origFn.apply( this, arguments );
5550            };
5551
5552            // Use same guid so caller can remove using origFn
5553            fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
5554        }
5555        return elem.each( function() {
5556            jQuery.event.add( this, types, fn, data, selector );
5557        } );
5558    }
5559
5560    /*
5561 * Helper functions for managing events -- not part of the public interface.
5562 * Props to Dean Edwards' addEvent library for many of the ideas.
5563 */
5564    jQuery.event = {
5565
5566        global: {},
5567
5568        add: function( elem, types, handler, data, selector ) {
5569
5570            var handleObjIn, eventHandle, tmp,
5571                events, t, handleObj,
5572                special, handlers, type, namespaces, origType,
5573                elemData = dataPriv.get( elem );
5574
5575            // Only attach events to objects that accept data
5576            if ( !acceptData( elem ) ) {
5577                return;
5578            }
5579
5580            // Caller can pass in an object of custom data in lieu of the handler
5581            if ( handler.handler ) {
5582                handleObjIn = handler;
5583                handler = handleObjIn.handler;
5584                selector = handleObjIn.selector;
5585            }
5586
5587            // Ensure that invalid selectors throw exceptions at attach time
5588            // Evaluate against documentElement in case elem is a non-element node (e.g., document)
5589            if ( selector ) {
5590                jQuery.find.matchesSelector( documentElement, selector );
5591            }
5592
5593            // Make sure that the handler has a unique ID, used to find/remove it later
5594            if ( !handler.guid ) {
5595                handler.guid = jQuery.guid++;
5596            }
5597
5598            // Init the element's event structure and main handler, if this is the first
5599            if ( !( events = elemData.events ) ) {
5600                events = elemData.events = Object.create( null );
5601            }
5602            if ( !( eventHandle = elemData.handle ) ) {
5603                eventHandle = elemData.handle = function( e ) {
5604
5605                    // Discard the second event of a jQuery.event.trigger() and
5606                    // when an event is called after a page has unloaded
5607                    return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
5608                        jQuery.event.dispatch.apply( elem, arguments ) : undefined;
5609                };
5610            }
5611
5612            // Handle multiple events separated by a space
5613            types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
5614            t = types.length;
5615            while ( t-- ) {
5616                tmp = rtypenamespace.exec( types[ t ] ) || [];
5617                type = origType = tmp[ 1 ];
5618                namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
5619
5620                // There *must* be a type, no attaching namespace-only handlers
5621                if ( !type ) {
5622                    continue;
5623                }
5624
5625                // If event changes its type, use the special event handlers for the changed type
5626                special = jQuery.event.special[ type ] || {};
5627
5628                // If selector defined, determine special event api type, otherwise given type
5629                type = ( selector ? special.delegateType : special.bindType ) || type;
5630
5631                // Update special based on newly reset type
5632                special = jQuery.event.special[ type ] || {};
5633
5634                // handleObj is passed to all event handlers
5635                handleObj = jQuery.extend( {
5636                    type: type,
5637                    origType: origType,
5638                    data: data,
5639                    handler: handler,
5640                    guid: handler.guid,
5641                    selector: selector,
5642                    needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
5643                    namespace: namespaces.join( "." )
5644                }, handleObjIn );
5645
5646                // Init the event handler queue if we're the first
5647                if ( !( handlers = events[ type ] ) ) {
5648                    handlers = events[ type ] = [];
5649                    handlers.delegateCount = 0;
5650
5651                    // Only use addEventListener if the special events handler returns false
5652                    if ( !special.setup ||
5653                        special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
5654
5655                        if ( elem.addEventListener ) {
5656                            elem.addEventListener( type, eventHandle );
5657                        }
5658                    }
5659                }
5660
5661                if ( special.add ) {
5662                    special.add.call( elem, handleObj );
5663
5664                    if ( !handleObj.handler.guid ) {
5665                        handleObj.handler.guid = handler.guid;
5666                    }
5667                }
5668
5669                // Add to the element's handler list, delegates in front
5670                if ( selector ) {
5671                    handlers.splice( handlers.delegateCount++, 0, handleObj );
5672                } else {
5673                    handlers.push( handleObj );
5674                }
5675
5676                // Keep track of which events have ever been used, for event optimization
5677                jQuery.event.global[ type ] = true;
5678            }
5679
5680        },
5681
5682        // Detach an event or set of events from an element
5683        remove: function( elem, types, handler, selector, mappedTypes ) {
5684
5685            var j, origCount, tmp,
5686                events, t, handleObj,
5687                special, handlers, type, namespaces, origType,
5688                elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
5689
5690            if ( !elemData || !( events = elemData.events ) ) {
5691                return;
5692            }
5693
vendor: 4,497 bytes, lines 5694-5797
5694            // Once for each type.namespace in types; type may be omitted
5695            types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
5696            t = types.length;
5697            while ( t-- ) {
5698                tmp = rtypenamespace.exec( types[ t ] ) || [];
5699                type = origType = tmp[ 1 ];
5700                namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
5701
5702                // Unbind all events (on this namespace, if provided) for the element
5703                if ( !type ) {
5704                    for ( type in events ) {
5705                        jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
5706                    }
5707                    continue;
5708                }
5709
5710                special = jQuery.event.special[ type ] || {};
5711                type = ( selector ? special.delegateType : special.bindType ) || type;
5712                handlers = events[ type ] || [];
5713                tmp = tmp[ 2 ] &&
5714                    new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
5715
5716                // Remove matching events
5717                origCount = j = handlers.length;
5718                while ( j-- ) {
5719                    handleObj = handlers[ j ];
5720
5721                    if ( ( mappedTypes || origType === handleObj.origType ) &&
5722                        ( !handler || handler.guid === handleObj.guid ) &&
5723                        ( !tmp || tmp.test( handleObj.namespace ) ) &&
5724                        ( !selector || selector === handleObj.selector ||
5725                            selector === "**" && handleObj.selector ) ) {
5726                        handlers.splice( j, 1 );
5727
5728                        if ( handleObj.selector ) {
5729                            handlers.delegateCount--;
5730                        }
5731                        if ( special.remove ) {
5732                            special.remove.call( elem, handleObj );
5733                        }
5734                    }
5735                }
5736
5737                // Remove generic event handler if we removed something and no more handlers exist
5738                // (avoids potential for endless recursion during removal of special event handlers)
5739                if ( origCount && !handlers.length ) {
5740                    if ( !special.teardown ||
5741                        special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
5742
5743                        jQuery.removeEvent( elem, type, elemData.handle );
5744                    }
5745
5746                    delete events[ type ];
5747                }
5748            }
5749
5750            // Remove data and the expando if it's no longer used
5751            if ( jQuery.isEmptyObject( events ) ) {
5752                dataPriv.remove( elem, "handle events" );
5753            }
5754        },
5755
5756        dispatch: function( nativeEvent ) {
5757
5758            var i, j, ret, matched, handleObj, handlerQueue,
5759                args = new Array( arguments.length ),
5760
5761                // Make a writable jQuery.Event from the native event object
5762                event = jQuery.event.fix( nativeEvent ),
5763
5764                handlers = (
5765                    dataPriv.get( this, "events" ) || Object.create( null )
5766                )[ event.type ] || [],
5767                special = jQuery.event.special[ event.type ] || {};
5768
5769            // Use the fix-ed jQuery.Event rather than the (read-only) native event
5770            args[ 0 ] = event;
5771
5772            for ( i = 1; i < arguments.length; i++ ) {
5773                args[ i ] = arguments[ i ];
5774            }
5775
5776            event.delegateTarget = this;
5777
5778            // Call the preDispatch hook for the mapped type, and let it bail if desired
5779            if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
5780                return;
5781            }
5782
5783            // Determine handlers
5784            handlerQueue = jQuery.event.handlers.call( this, event, handlers );
5785
5786            // Run delegates first; they may want to stop propagation beneath us
5787            i = 0;
5788            while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
5789                event.currentTarget = matched.elem;
5790
5791                j = 0;
5792                while ( ( handleObj = matched.handlers[ j++ ] ) &&
5793                !event.isImmediatePropagationStopped() ) {
5794
5795                    // If the event is namespaced, then each handler is only invoked if it is
5796                    // specially universal or its namespaces are a superset of the event's.
5797                    if ( !event.rnamespace || handleObj.namespace === false ||
vendor: 15,374 bytes, lines 5798-6184
5798                        event.rnamespace.test( handleObj.namespace ) ) {
5799
5800                        event.handleObj = handleObj;
5801                        event.data = handleObj.data;
5802
5803                        ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
5804                            handleObj.handler ).apply( matched.elem, args );
5805
5806                        if ( ret !== undefined ) {
5807                            if ( ( event.result = ret ) === false ) {
5808                                event.preventDefault();
5809                                event.stopPropagation();
5810                            }
5811                        }
5812                    }
5813                }
5814            }
5815
5816            // Call the postDispatch hook for the mapped type
5817            if ( special.postDispatch ) {
5818                special.postDispatch.call( this, event );
5819            }
5820
5821            return event.result;
5822        },
5823
5824        handlers: function( event, handlers ) {
5825            var i, handleObj, sel, matchedHandlers, matchedSelectors,
5826                handlerQueue = [],
5827                delegateCount = handlers.delegateCount,
5828                cur = event.target;
5829
5830            // Find delegate handlers
5831            if ( delegateCount &&
5832
5833                // Support: IE <=9
5834                // Black-hole SVG <use> instance trees (trac-13180)
5835                cur.nodeType &&
5836
5837                // Support: Firefox <=42
5838                // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
5839                // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
5840                // Support: IE 11 only
5841                // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
5842                !( event.type === "click" && event.button >= 1 ) ) {
5843
5844                for ( ; cur !== this; cur = cur.parentNode || this ) {
5845
5846                    // Don't check non-elements (#13208)
5847                    // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
5848                    if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
5849                        matchedHandlers = [];
5850                        matchedSelectors = {};
5851                        for ( i = 0; i < delegateCount; i++ ) {
5852                            handleObj = handlers[ i ];
5853
5854                            // Don't conflict with Object.prototype properties (#13203)
5855                            sel = handleObj.selector + " ";
5856
5857                            if ( matchedSelectors[ sel ] === undefined ) {
5858                                matchedSelectors[ sel ] = handleObj.needsContext ?
5859                                    jQuery( sel, this ).index( cur ) > -1 :
5860                                    jQuery.find( sel, this, null, [ cur ] ).length;
5861                            }
5862                            if ( matchedSelectors[ sel ] ) {
5863                                matchedHandlers.push( handleObj );
5864                            }
5865                        }
5866                        if ( matchedHandlers.length ) {
5867                            handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
5868                        }
5869                    }
5870                }
5871            }
5872
5873            // Add the remaining (directly-bound) handlers
5874            cur = this;
5875            if ( delegateCount < handlers.length ) {
5876                handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
5877            }
5878
5879            return handlerQueue;
5880        },
5881
5882        addProp: function( name, hook ) {
5883            Object.defineProperty( jQuery.Event.prototype, name, {
5884                enumerable: true,
5885                configurable: true,
5886
5887                get: isFunction( hook ) ?
5888                    function() {
5889                        if ( this.originalEvent ) {
5890                            return hook( this.originalEvent );
5891                        }
5892                    } :
5893                    function() {
5894                        if ( this.originalEvent ) {
5895                            return this.originalEvent[ name ];
5896                        }
5897                    },
5898
5899                set: function( value ) {
5900                    Object.defineProperty( this, name, {
5901                        enumerable: true,
5902                        configurable: true,
5903                        writable: true,
5904                        value: value
5905                    } );
5906                }
5907            } );
5908        },
5909
5910        fix: function( originalEvent ) {
5911            return originalEvent[ jQuery.expando ] ?
5912                originalEvent :
5913                new jQuery.Event( originalEvent );
5914        },
5915
5916        special: {
5917            load: {
5918
5919                // Prevent triggered image.load events from bubbling to window.load
5920                noBubble: true
5921            },
5922            click: {
5923
5924                // Utilize native event to ensure correct state for checkable inputs
5925                setup: function( data ) {
5926
5927                    // For mutual compressibility with _default, replace `this` access with a local var.
5928                    // `|| data` is dead code meant only to preserve the variable through minification.
5929                    var el = this || data;
5930
5931                    // Claim the first handler
5932                    if ( rcheckableType.test( el.type ) &&
5933                        el.click && nodeName( el, "input" ) ) {
5934
5935                        // dataPriv.set( el, "click", ... )
5936                        leverageNative( el, "click", returnTrue );
5937                    }
5938
5939                    // Return false to allow normal processing in the caller
5940                    return false;
5941                },
5942                trigger: function( data ) {
5943
5944                    // For mutual compressibility with _default, replace `this` access with a local var.
5945                    // `|| data` is dead code meant only to preserve the variable through minification.
5946                    var el = this || data;
5947
5948                    // Force setup before triggering a click
5949                    if ( rcheckableType.test( el.type ) &&
5950                        el.click && nodeName( el, "input" ) ) {
5951
5952                        leverageNative( el, "click" );
5953                    }
5954
5955                    // Return non-false to allow normal event-path propagation
5956                    return true;
5957                },
5958
5959                // For cross-browser consistency, suppress native .click() on links
5960                // Also prevent it if we're currently inside a leveraged native-event stack
5961                _default: function( event ) {
5962                    var target = event.target;
5963                    return rcheckableType.test( target.type ) &&
5964                        target.click && nodeName( target, "input" ) &&
5965                        dataPriv.get( target, "click" ) ||
5966                        nodeName( target, "a" );
5967                }
5968            },
5969
5970            beforeunload: {
5971                postDispatch: function( event ) {
5972
5973                    // Support: Firefox 20+
5974                    // Firefox doesn't alert if the returnValue field is not set.
5975                    if ( event.result !== undefined && event.originalEvent ) {
5976                        event.originalEvent.returnValue = event.result;
5977                    }
5978                }
5979            }
5980        }
5981    };
5982
5983// Ensure the presence of an event listener that handles manually-triggered
5984// synthetic events by interrupting progress until reinvoked in response to
5985// *native* events that it fires directly, ensuring that state changes have
5986// already occurred before other listeners are invoked.
5987    function leverageNative( el, type, expectSync ) {
5988
5989        // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
5990        if ( !expectSync ) {
5991            if ( dataPriv.get( el, type ) === undefined ) {
5992                jQuery.event.add( el, type, returnTrue );
5993            }
5994            return;
5995        }
5996
5997        // Register the controller as a special universal handler for all event namespaces
5998        dataPriv.set( el, type, false );
5999        jQuery.event.add( el, type, {
6000            namespace: false,
6001            handler: function( event ) {
6002                var notAsync, result,
6003                    saved = dataPriv.get( this, type );
6004
6005                if ( ( event.isTrigger & 1 ) && this[ type ] ) {
6006
6007                    // Interrupt processing of the outer synthetic .trigger()ed event
6008                    // Saved data should be false in such cases, but might be a leftover capture object
6009                    // from an async native handler (gh-4350)
6010                    if ( !saved.length ) {
6011
6012                        // Store arguments for use when handling the inner native event
6013                        // There will always be at least one argument (an event object), so this array
6014                        // will not be confused with a leftover capture object.
6015                        saved = slice.call( arguments );
6016                        dataPriv.set( this, type, saved );
6017
6018                        // Trigger the native event and capture its result
6019                        // Support: IE <=9 - 11+
6020                        // focus() and blur() are asynchronous
6021                        notAsync = expectSync( this, type );
6022                        this[ type ]();
6023                        result = dataPriv.get( this, type );
6024                        if ( saved !== result || notAsync ) {
6025                            dataPriv.set( this, type, false );
6026                        } else {
6027                            result = {};
6028                        }
6029                        if ( saved !== result ) {
6030
6031                            // Cancel the outer synthetic event
6032                            event.stopImmediatePropagation();
6033                            event.preventDefault();
6034
6035                            // Support: Chrome 86+
6036                            // In Chrome, if an element having a focusout handler is blurred by
6037                            // clicking outside of it, it invokes the handler synchronously. If
6038                            // that handler calls `.remove()` on the element, the data is cleared,
6039                            // leaving `result` undefined. We need to guard against this.
6040                            return result && result.value;
6041                        }
6042
6043                        // If this is an inner synthetic event for an event with a bubbling surrogate
6044                        // (focus or blur), assume that the surrogate already propagated from triggering the
6045                        // native event and prevent that from happening again here.
6046                        // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
6047                        // bubbling surrogate propagates *after* the non-bubbling base), but that seems
6048                        // less bad than duplication.
6049                    } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
6050                        event.stopPropagation();
6051                    }
6052
6053                    // If this is a native event triggered above, everything is now in order
6054                    // Fire an inner synthetic event with the original arguments
6055                } else if ( saved.length ) {
6056
6057                    // ...and capture the result
6058                    dataPriv.set( this, type, {
6059                        value: jQuery.event.trigger(
6060
6061                            // Support: IE <=9 - 11+
6062                            // Extend with the prototype to reset the above stopImmediatePropagation()
6063                            jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
6064                            saved.slice( 1 ),
6065                            this
6066                        )
6067                    } );
6068
6069                    // Abort handling of the native event
6070                    event.stopImmediatePropagation();
6071                }
6072            }
6073        } );
6074    }
6075
6076    jQuery.removeEvent = function( elem, type, handle ) {
6077
6078        // This "if" is needed for plain objects
6079        if ( elem.removeEventListener ) {
6080            elem.removeEventListener( type, handle );
6081        }
6082    };
6083
6084    jQuery.Event = function( src, props ) {
6085
6086        // Allow instantiation without the 'new' keyword
6087        if ( !( this instanceof jQuery.Event ) ) {
6088            return new jQuery.Event( src, props );
6089        }
6090
6091        // Event object
6092        if ( src && src.type ) {
6093            this.originalEvent = src;
6094            this.type = src.type;
6095
6096            // Events bubbling up the document may have been marked as prevented
6097            // by a handler lower down the tree; reflect the correct value.
6098            this.isDefaultPrevented = src.defaultPrevented ||
6099            src.defaultPrevented === undefined &&
6100
6101            // Support: Android <=2.3 only
6102            src.returnValue === false ?
6103                returnTrue :
6104                returnFalse;
6105
6106            // Create target properties
6107            // Support: Safari <=6 - 7 only
6108            // Target should not be a text node (#504, #13143)
6109            this.target = ( src.target && src.target.nodeType === 3 ) ?
6110                src.target.parentNode :
6111                src.target;
6112
6113            this.currentTarget = src.currentTarget;
6114            this.relatedTarget = src.relatedTarget;
6115
6116            // Event type
6117        } else {
6118            this.type = src;
6119        }
6120
6121        // Put explicitly provided properties onto the event object
6122        if ( props ) {
6123            jQuery.extend( this, props );
6124        }
6125
6126        // Create a timestamp if incoming event doesn't have one
6127        this.timeStamp = src && src.timeStamp || Date.now();
6128
6129        // Mark it as fixed
6130        this[ jQuery.expando ] = true;
6131    };
6132
6133// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
6134// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
6135    jQuery.Event.prototype = {
6136        constructor: jQuery.Event,
6137        isDefaultPrevented: returnFalse,
6138        isPropagationStopped: returnFalse,
6139        isImmediatePropagationStopped: returnFalse,
6140        isSimulated: false,
6141
6142        preventDefault: function() {
6143            var e = this.originalEvent;
6144
6145            this.isDefaultPrevented = returnTrue;
6146
6147            if ( e && !this.isSimulated ) {
6148                e.preventDefault();
6149            }
6150        },
6151        stopPropagation: function() {
6152            var e = this.originalEvent;
6153
6154            this.isPropagationStopped = returnTrue;
6155
6156            if ( e && !this.isSimulated ) {
6157                e.stopPropagation();
6158            }
6159        },
6160        stopImmediatePropagation: function() {
6161            var e = this.originalEvent;
6162
6163            this.isImmediatePropagationStopped = returnTrue;
6164
6165            if ( e && !this.isSimulated ) {
6166                e.stopImmediatePropagation();
6167            }
6168
6169            this.stopPropagation();
6170        }
6171    };
6172
6173// Includes all common event props including KeyEvent and MouseEvent specific props
6174    jQuery.each( {
6175        altKey: true,
6176        bubbles: true,
6177        cancelable: true,
6178        changedTouches: true,
6179        ctrlKey: true,
6180        detail: true,
6181        eventPhase: true,
6182        metaKey: true,
6183        pageX: true,
6184        pageY: true,
vendor: 4,565 bytes, lines 6185-6325
6185        shiftKey: true,
6186        view: true,
6187        "char": true,
6188        code: true,
6189        charCode: true,
6190        key: true,
6191        keyCode: true,
6192        button: true,
6193        buttons: true,
6194        clientX: true,
6195        clientY: true,
6196        offsetX: true,
6197        offsetY: true,
6198        pointerId: true,
6199        pointerType: true,
6200        screenX: true,
6201        screenY: true,
6202        targetTouches: true,
6203        toElement: true,
6204        touches: true,
6205        which: true
6206    }, jQuery.event.addProp );
6207
6208    jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
6209        jQuery.event.special[ type ] = {
6210
6211            // Utilize native event if possible so blur/focus sequence is correct
6212            setup: function() {
6213
6214                // Claim the first handler
6215                // dataPriv.set( this, "focus", ... )
6216                // dataPriv.set( this, "blur", ... )
6217                leverageNative( this, type, expectSync );
6218
6219                // Return false to allow normal processing in the caller
6220                return false;
6221            },
6222            trigger: function() {
6223
6224                // Force setup before trigger
6225                leverageNative( this, type );
6226
6227                // Return non-false to allow normal event-path propagation
6228                return true;
6229            },
6230
6231            // Suppress native focus or blur as it's already being fired
6232            // in leverageNative.
6233            _default: function() {
6234                return true;
6235            },
6236
6237            delegateType: delegateType
6238        };
6239    } );
6240
6241// Create mouseenter/leave events using mouseover/out and event-time checks
6242// so that event delegation works in jQuery.
6243// Do the same for pointerenter/pointerleave and pointerover/pointerout
6244//
6245// Support: Safari 7 only
6246// Safari sends mouseenter too often; see:
6247// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
6248// for the description of the bug (it existed in older Chrome versions as well).
6249    jQuery.each( {
6250        mouseenter: "mouseover",
6251        mouseleave: "mouseout",
6252        pointerenter: "pointerover",
6253        pointerleave: "pointerout"
6254    }, function( orig, fix ) {
6255        jQuery.event.special[ orig ] = {
6256            delegateType: fix,
6257            bindType: fix,
6258
6259            handle: function( event ) {
6260                var ret,
6261                    target = this,
6262                    related = event.relatedTarget,
6263                    handleObj = event.handleObj;
6264
6265                // For mouseenter/leave call the handler if related is outside the target.
6266                // NB: No relatedTarget if the mouse left/entered the browser window
6267                if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
6268                    event.type = handleObj.origType;
6269                    ret = handleObj.handler.apply( this, arguments );
6270                    event.type = fix;
6271                }
6272                return ret;
6273            }
6274        };
6275    } );
6276
6277    jQuery.fn.extend( {
6278
6279        on: function( types, selector, data, fn ) {
6280            return on( this, types, selector, data, fn );
6281        },
6282        one: function( types, selector, data, fn ) {
6283            return on( this, types, selector, data, fn, 1 );
6284        },
6285        off: function( types, selector, fn ) {
6286            var handleObj, type;
6287            if ( types && types.preventDefault && types.handleObj ) {
6288
6289                // ( event )  dispatched jQuery.Event
6290                handleObj = types.handleObj;
6291                jQuery( types.delegateTarget ).off(
6292                    handleObj.namespace ?
6293                        handleObj.origType + "." + handleObj.namespace :
6294                        handleObj.origType,
6295                    handleObj.selector,
6296                    handleObj.handler
6297                );
6298                return this;
6299            }
6300            if ( typeof types === "object" ) {
6301
6302                // ( types-object [, selector] )
6303                for ( type in types ) {
6304                    this.off( type, selector, types[ type ] );
6305                }
6306                return this;
6307            }
6308            if ( selector === false || typeof selector === "function" ) {
6309
6310                // ( types [, fn] )
6311                fn = selector;
6312                selector = undefined;
6313            }
6314            if ( fn === false ) {
6315                fn = returnFalse;
6316            }
6317            return this.each( function() {
6318                jQuery.event.remove( this, types, fn, selector );
6319            } );
6320        }
6321    } );
6322
6323
6324    var
6325
vendor: 9,664 bytes, lines 6326-6586
6326        // Support: IE <=10 - 11, Edge 12 - 13 only
6327        // In IE/Edge using regex groups here causes severe slowdowns.
6328        // See https://connect.microsoft.com/IE/feedback/details/1736512/
6329        rnoInnerhtml = /<script|<style|<link/i,
6330
6331        // checked="checked" or checked
6332        rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
6333        rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
6334
6335// Prefer a tbody over its parent table for containing new rows
6336    function manipulationTarget( elem, content ) {
6337        if ( nodeName( elem, "table" ) &&
6338            nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
6339
6340            return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
6341        }
6342
6343        return elem;
6344    }
6345
6346// Replace/restore the type attribute of script elements for safe DOM manipulation
6347    function disableScript( elem ) {
6348        elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
6349        return elem;
6350    }
6351    function restoreScript( elem ) {
6352        if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
6353            elem.type = elem.type.slice( 5 );
6354        } else {
6355            elem.removeAttribute( "type" );
6356        }
6357
6358        return elem;
6359    }
6360
6361    function cloneCopyEvent( src, dest ) {
6362        var i, l, type, pdataOld, udataOld, udataCur, events;
6363
6364        if ( dest.nodeType !== 1 ) {
6365            return;
6366        }
6367
6368        // 1. Copy private data: events, handlers, etc.
6369        if ( dataPriv.hasData( src ) ) {
6370            pdataOld = dataPriv.get( src );
6371            events = pdataOld.events;
6372
6373            if ( events ) {
6374                dataPriv.remove( dest, "handle events" );
6375
6376                for ( type in events ) {
6377                    for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6378                        jQuery.event.add( dest, type, events[ type ][ i ] );
6379                    }
6380                }
6381            }
6382        }
6383
6384        // 2. Copy user data
6385        if ( dataUser.hasData( src ) ) {
6386            udataOld = dataUser.access( src );
6387            udataCur = jQuery.extend( {}, udataOld );
6388
6389            dataUser.set( dest, udataCur );
6390        }
6391    }
6392
6393// Fix IE bugs, see support tests
6394    function fixInput( src, dest ) {
6395        var nodeName = dest.nodeName.toLowerCase();
6396
6397        // Fails to persist the checked state of a cloned checkbox or radio button.
6398        if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
6399            dest.checked = src.checked;
6400
6401            // Fails to return the selected option to the default selected state when cloning options
6402        } else if ( nodeName === "input" || nodeName === "textarea" ) {
6403            dest.defaultValue = src.defaultValue;
6404        }
6405    }
6406
6407    function domManip( collection, args, callback, ignored ) {
6408
6409        // Flatten any nested arrays
6410        args = flat( args );
6411
6412        var fragment, first, scripts, hasScripts, node, doc,
6413            i = 0,
6414            l = collection.length,
6415            iNoClone = l - 1,
6416            value = args[ 0 ],
6417            valueIsFunction = isFunction( value );
6418
6419        // We can't cloneNode fragments that contain checked, in WebKit
6420        if ( valueIsFunction ||
6421            ( l > 1 && typeof value === "string" &&
6422                !support.checkClone && rchecked.test( value ) ) ) {
6423            return collection.each( function( index ) {
6424                var self = collection.eq( index );
6425                if ( valueIsFunction ) {
6426                    args[ 0 ] = value.call( this, index, self.html() );
6427                }
6428                domManip( self, args, callback, ignored );
6429            } );
6430        }
6431
6432        if ( l ) {
6433            fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
6434            first = fragment.firstChild;
6435
6436            if ( fragment.childNodes.length === 1 ) {
6437                fragment = first;
6438            }
6439
6440            // Require either new content or an interest in ignored elements to invoke the callback
6441            if ( first || ignored ) {
6442                scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
6443                hasScripts = scripts.length;
6444
6445                // Use the original fragment for the last item
6446                // instead of the first because it can end up
6447                // being emptied incorrectly in certain situations (#8070).
6448                for ( ; i < l; i++ ) {
6449                    node = fragment;
6450
6451                    if ( i !== iNoClone ) {
6452                        node = jQuery.clone( node, true, true );
6453
6454                        // Keep references to cloned scripts for later restoration
6455                        if ( hasScripts ) {
6456
6457                            // Support: Android <=4.0 only, PhantomJS 1 only
6458                            // push.apply(_, arraylike) throws on ancient WebKit
6459                            jQuery.merge( scripts, getAll( node, "script" ) );
6460                        }
6461                    }
6462
6463                    callback.call( collection[ i ], node, i );
6464                }
6465
6466                if ( hasScripts ) {
6467                    doc = scripts[ scripts.length - 1 ].ownerDocument;
6468
6469                    // Reenable scripts
6470                    jQuery.map( scripts, restoreScript );
6471
6472                    // Evaluate executable scripts on first document insertion
6473                    for ( i = 0; i < hasScripts; i++ ) {
6474                        node = scripts[ i ];
6475                        if ( rscriptType.test( node.type || "" ) &&
6476                            !dataPriv.access( node, "globalEval" ) &&
6477                            jQuery.contains( doc, node ) ) {
6478
6479                            if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {
6480
6481                                // Optional AJAX dependency, but won't run scripts if not present
6482                                if ( jQuery._evalUrl && !node.noModule ) {
6483                                    jQuery._evalUrl( node.src, {
6484                                        nonce: node.nonce || node.getAttribute( "nonce" )
6485                                    }, doc );
6486                                }
6487                            } else {
6488                                DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
6489                            }
6490                        }
6491                    }
6492                }
6493            }
6494        }
6495
6496        return collection;
6497    }
6498
6499    function remove( elem, selector, keepData ) {
6500        var node,
6501            nodes = selector ? jQuery.filter( selector, elem ) : elem,
6502            i = 0;
6503
6504        for ( ; ( node = nodes[ i ] ) != null; i++ ) {
6505            if ( !keepData && node.nodeType === 1 ) {
6506                jQuery.cleanData( getAll( node ) );
6507            }
6508
6509            if ( node.parentNode ) {
6510                if ( keepData && isAttached( node ) ) {
6511                    setGlobalEval( getAll( node, "script" ) );
6512                }
6513                node.parentNode.removeChild( node );
6514            }
6515        }
6516
6517        return elem;
6518    }
6519
6520    jQuery.extend( {
6521        htmlPrefilter: function( html ) {
6522            return html;
6523        },
6524
6525        clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6526            var i, l, srcElements, destElements,
6527                clone = elem.cloneNode( true ),
6528                inPage = isAttached( elem );
6529
6530            // Fix IE cloning issues
6531            if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
6532                !jQuery.isXMLDoc( elem ) ) {
6533
6534                // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
6535                destElements = getAll( clone );
6536                srcElements = getAll( elem );
6537
6538                for ( i = 0, l = srcElements.length; i < l; i++ ) {
6539                    fixInput( srcElements[ i ], destElements[ i ] );
6540                }
6541            }
6542
6543            // Copy the events from the original to the clone
6544            if ( dataAndEvents ) {
6545                if ( deepDataAndEvents ) {
6546                    srcElements = srcElements || getAll( elem );
6547                    destElements = destElements || getAll( clone );
6548
6549                    for ( i = 0, l = srcElements.length; i < l; i++ ) {
6550                        cloneCopyEvent( srcElements[ i ], destElements[ i ] );
6551                    }
6552                } else {
6553                    cloneCopyEvent( elem, clone );
6554                }
6555            }
6556
6557            // Preserve script evaluation history
6558            destElements = getAll( clone, "script" );
6559            if ( destElements.length > 0 ) {
6560                setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
6561            }
6562
6563            // Return the cloned set
6564            return clone;
6565        },
6566
6567        cleanData: function( elems ) {
6568            var data, elem, type,
6569                special = jQuery.event.special,
6570                i = 0;
6571
6572            for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
6573                if ( acceptData( elem ) ) {
6574                    if ( ( data = elem[ dataPriv.expando ] ) ) {
6575                        if ( data.events ) {
6576                            for ( type in data.events ) {
6577                                if ( special[ type ] ) {
6578                                    jQuery.event.remove( elem, type );
6579
6580                                    // This is a shortcut to avoid jQuery.event.remove's overhead
6581                                } else {
6582                                    jQuery.removeEvent( elem, type, data.handle );
6583                                }
6584                            }
6585                        }
6586
vendor: 5,407 bytes, lines 6587-6743
6587                        // Support: Chrome <=35 - 45+
6588                        // Assign undefined instead of using delete, see Data#remove
6589                        elem[ dataPriv.expando ] = undefined;
6590                    }
6591                    if ( elem[ dataUser.expando ] ) {
6592
6593                        // Support: Chrome <=35 - 45+
6594                        // Assign undefined instead of using delete, see Data#remove
6595                        elem[ dataUser.expando ] = undefined;
6596                    }
6597                }
6598            }
6599        }
6600    } );
6601
6602    jQuery.fn.extend( {
6603        detach: function( selector ) {
6604            return remove( this, selector, true );
6605        },
6606
6607        remove: function( selector ) {
6608            return remove( this, selector );
6609        },
6610
6611        text: function( value ) {
6612            return access( this, function( value ) {
6613                return value === undefined ?
6614                    jQuery.text( this ) :
6615                    this.empty().each( function() {
6616                        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6617                            this.textContent = value;
6618                        }
6619                    } );
6620            }, null, value, arguments.length );
6621        },
6622
6623        append: function() {
6624            return domManip( this, arguments, function( elem ) {
6625                if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6626                    var target = manipulationTarget( this, elem );
6627                    target.appendChild( elem );
6628                }
6629            } );
6630        },
6631
6632        prepend: function() {
6633            return domManip( this, arguments, function( elem ) {
6634                if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6635                    var target = manipulationTarget( this, elem );
6636                    target.insertBefore( elem, target.firstChild );
6637                }
6638            } );
6639        },
6640
6641        before: function() {
6642            return domManip( this, arguments, function( elem ) {
6643                if ( this.parentNode ) {
6644                    this.parentNode.insertBefore( elem, this );
6645                }
6646            } );
6647        },
6648
6649        after: function() {
6650            return domManip( this, arguments, function( elem ) {
6651                if ( this.parentNode ) {
6652                    this.parentNode.insertBefore( elem, this.nextSibling );
6653                }
6654            } );
6655        },
6656
6657        empty: function() {
6658            var elem,
6659                i = 0;
6660
6661            for ( ; ( elem = this[ i ] ) != null; i++ ) {
6662                if ( elem.nodeType === 1 ) {
6663
6664                    // Prevent memory leaks
6665                    jQuery.cleanData( getAll( elem, false ) );
6666
6667                    // Remove any remaining nodes
6668                    elem.textContent = "";
6669                }
6670            }
6671
6672            return this;
6673        },
6674
6675        clone: function( dataAndEvents, deepDataAndEvents ) {
6676            dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6677            deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6678
6679            return this.map( function() {
6680                return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6681            } );
6682        },
6683
6684        html: function( value ) {
6685            return access( this, function( value ) {
6686                var elem = this[ 0 ] || {},
6687                    i = 0,
6688                    l = this.length;
6689
6690                if ( value === undefined && elem.nodeType === 1 ) {
6691                    return elem.innerHTML;
6692                }
6693
6694                // See if we can take a shortcut and just use innerHTML
6695                if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6696                    !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
6697
6698                    value = jQuery.htmlPrefilter( value );
6699
6700                    try {
6701                        for ( ; i < l; i++ ) {
6702                            elem = this[ i ] || {};
6703
6704                            // Remove element nodes and prevent memory leaks
6705                            if ( elem.nodeType === 1 ) {
6706                                jQuery.cleanData( getAll( elem, false ) );
6707                                elem.innerHTML = value;
6708                            }
6709                        }
6710
6711                        elem = 0;
6712
6713                        // If using innerHTML throws an exception, use the fallback method
6714                    } catch ( e ) {}
6715                }
6716
6717                if ( elem ) {
6718                    this.empty().append( value );
6719                }
6720            }, null, value, arguments.length );
6721        },
6722
6723        replaceWith: function() {
6724            var ignored = [];
6725
6726            // Make the changes, replacing each non-ignored context element with the new content
6727            return domManip( this, arguments, function( elem ) {
6728                var parent = this.parentNode;
6729
6730                if ( jQuery.inArray( this, ignored ) < 0 ) {
6731                    jQuery.cleanData( getAll( this ) );
6732                    if ( parent ) {
6733                        parent.replaceChild( elem, this );
6734                    }
6735                }
6736
6737                // Force callback invocation
6738            }, ignored );
6739        }
6740    } );
6741
6742    jQuery.each( {
6743        appendTo: "append",
vendor: 15,461 bytes, lines 6744-7163
6744        prependTo: "prepend",
6745        insertBefore: "before",
6746        insertAfter: "after",
6747        replaceAll: "replaceWith"
6748    }, function( name, original ) {
6749        jQuery.fn[ name ] = function( selector ) {
6750            var elems,
6751                ret = [],
6752                insert = jQuery( selector ),
6753                last = insert.length - 1,
6754                i = 0;
6755
6756            for ( ; i <= last; i++ ) {
6757                elems = i === last ? this : this.clone( true );
6758                jQuery( insert[ i ] )[ original ]( elems );
6759
6760                // Support: Android <=4.0 only, PhantomJS 1 only
6761                // .get() because push.apply(_, arraylike) throws on ancient WebKit
6762                push.apply( ret, elems.get() );
6763            }
6764
6765            return this.pushStack( ret );
6766        };
6767    } );
6768    var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
6769
6770    var getStyles = function( elem ) {
6771
6772        // Support: IE <=11 only, Firefox <=30 (#15098, #14150)
6773        // IE throws on elements created in popups
6774        // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
6775        var view = elem.ownerDocument.defaultView;
6776
6777        if ( !view || !view.opener ) {
6778            view = window;
6779        }
6780
6781        return view.getComputedStyle( elem );
6782    };
6783
6784    var swap = function( elem, options, callback ) {
6785        var ret, name,
6786            old = {};
6787
6788        // Remember the old values, and insert the new ones
6789        for ( name in options ) {
6790            old[ name ] = elem.style[ name ];
6791            elem.style[ name ] = options[ name ];
6792        }
6793
6794        ret = callback.call( elem );
6795
6796        // Revert the old values
6797        for ( name in options ) {
6798            elem.style[ name ] = old[ name ];
6799        }
6800
6801        return ret;
6802    };
6803
6804
6805    var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
6806
6807
6808
6809    ( function() {
6810
6811        // Executing both pixelPosition & boxSizingReliable tests require only one layout
6812        // so they're executed at the same time to save the second computation.
6813        function computeStyleTests() {
6814
6815            // This is a singleton, we need to execute it only once
6816            if ( !div ) {
6817                return;
6818            }
6819
6820            container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
6821                "margin-top:1px;padding:0;border:0";
6822            div.style.cssText =
6823                "position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
6824                "margin:auto;border:1px;padding:1px;" +
6825                "width:60%;top:1%";
6826            documentElement.appendChild( container ).appendChild( div );
6827
6828            var divStyle = window.getComputedStyle( div );
6829            pixelPositionVal = divStyle.top !== "1%";
6830
6831            // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
6832            reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
6833
6834            // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
6835            // Some styles come back with percentage values, even though they shouldn't
6836            div.style.right = "60%";
6837            pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
6838
6839            // Support: IE 9 - 11 only
6840            // Detect misreporting of content dimensions for box-sizing:border-box elements
6841            boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
6842
6843            // Support: IE 9 only
6844            // Detect overflow:scroll screwiness (gh-3699)
6845            // Support: Chrome <=64
6846            // Don't get tricked when zoom affects offsetWidth (gh-4029)
6847            div.style.position = "absolute";
6848            scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;
6849
6850            documentElement.removeChild( container );
6851
6852            // Nullify the div so it wouldn't be stored in the memory and
6853            // it will also be a sign that checks already performed
6854            div = null;
6855        }
6856
6857        function roundPixelMeasures( measure ) {
6858            return Math.round( parseFloat( measure ) );
6859        }
6860
6861        var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
6862            reliableTrDimensionsVal, reliableMarginLeftVal,
6863            container = document.createElement( "div" ),
6864            div = document.createElement( "div" );
6865
6866        // Finish early in limited (non-browser) environments
6867        if ( !div.style ) {
6868            return;
6869        }
6870
6871        // Support: IE <=9 - 11 only
6872        // Style of cloned element affects source element cloned (#8908)
6873        div.style.backgroundClip = "content-box";
6874        div.cloneNode( true ).style.backgroundClip = "";
6875        support.clearCloneStyle = div.style.backgroundClip === "content-box";
6876
6877        jQuery.extend( support, {
6878            boxSizingReliable: function() {
6879                computeStyleTests();
6880                return boxSizingReliableVal;
6881            },
6882            pixelBoxStyles: function() {
6883                computeStyleTests();
6884                return pixelBoxStylesVal;
6885            },
6886            pixelPosition: function() {
6887                computeStyleTests();
6888                return pixelPositionVal;
6889            },
6890            reliableMarginLeft: function() {
6891                computeStyleTests();
6892                return reliableMarginLeftVal;
6893            },
6894            scrollboxSize: function() {
6895                computeStyleTests();
6896                return scrollboxSizeVal;
6897            },
6898
6899            // Support: IE 9 - 11+, Edge 15 - 18+
6900            // IE/Edge misreport `getComputedStyle` of table rows with width/height
6901            // set in CSS while `offset*` properties report correct values.
6902            // Behavior in IE 9 is more subtle than in newer versions & it passes
6903            // some versions of this test; make sure not to make it pass there!
6904            //
6905            // Support: Firefox 70+
6906            // Only Firefox includes border widths
6907            // in computed dimensions. (gh-4529)
6908            reliableTrDimensions: function() {
6909                var table, tr, trChild, trStyle;
6910                if ( reliableTrDimensionsVal == null ) {
6911                    table = document.createElement( "table" );
6912                    tr = document.createElement( "tr" );
6913                    trChild = document.createElement( "div" );
6914
6915                    table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
6916                    tr.style.cssText = "border:1px solid";
6917
6918                    // Support: Chrome 86+
6919                    // Height set through cssText does not get applied.
6920                    // Computed height then comes back as 0.
6921                    tr.style.height = "1px";
6922                    trChild.style.height = "9px";
6923
6924                    // Support: Android 8 Chrome 86+
6925                    // In our bodyBackground.html iframe,
6926                    // display for all div elements is set to "inline",
6927                    // which causes a problem only in Android 8 Chrome 86.
6928                    // Ensuring the div is display: block
6929                    // gets around this issue.
6930                    trChild.style.display = "block";
6931
6932                    documentElement
6933                        .appendChild( table )
6934                        .appendChild( tr )
6935                        .appendChild( trChild );
6936
6937                    trStyle = window.getComputedStyle( tr );
6938                    reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +
6939                        parseInt( trStyle.borderTopWidth, 10 ) +
6940                        parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;
6941
6942                    documentElement.removeChild( table );
6943                }
6944                return reliableTrDimensionsVal;
6945            }
6946        } );
6947    } )();
6948
6949
6950    function curCSS( elem, name, computed ) {
6951        var width, minWidth, maxWidth, ret,
6952
6953            // Support: Firefox 51+
6954            // Retrieving style before computed somehow
6955            // fixes an issue with getting wrong values
6956            // on detached elements
6957            style = elem.style;
6958
6959        computed = computed || getStyles( elem );
6960
6961        // getPropertyValue is needed for:
6962        //   .css('filter') (IE 9 only, #12537)
6963        //   .css('--customProperty) (#3144)
6964        if ( computed ) {
6965            ret = computed.getPropertyValue( name ) || computed[ name ];
6966
6967            if ( ret === "" && !isAttached( elem ) ) {
6968                ret = jQuery.style( elem, name );
6969            }
6970
6971            // A tribute to the "awesome hack by Dean Edwards"
6972            // Android Browser returns percentage for some values,
6973            // but width seems to be reliably pixels.
6974            // This is against the CSSOM draft spec:
6975            // https://drafts.csswg.org/cssom/#resolved-values
6976            if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
6977
6978                // Remember the original values
6979                width = style.width;
6980                minWidth = style.minWidth;
6981                maxWidth = style.maxWidth;
6982
6983                // Put in the new values to get a computed value out
6984                style.minWidth = style.maxWidth = style.width = ret;
6985                ret = computed.width;
6986
6987                // Revert the changed values
6988                style.width = width;
6989                style.minWidth = minWidth;
6990                style.maxWidth = maxWidth;
6991            }
6992        }
6993
6994        return ret !== undefined ?
6995
6996            // Support: IE <=9 - 11 only
6997            // IE returns zIndex value as an integer.
6998            ret + "" :
6999            ret;
7000    }
7001
7002
7003    function addGetHookIf( conditionFn, hookFn ) {
7004
7005        // Define the hook, we'll check on the first run if it's really needed.
7006        return {
7007            get: function() {
7008                if ( conditionFn() ) {
7009
7010                    // Hook not needed (or it's not possible to use it due
7011                    // to missing dependency), remove it.
7012                    delete this.get;
7013                    return;
7014                }
7015
7016                // Hook needed; redefine it so that the support test is not executed again.
7017                return ( this.get = hookFn ).apply( this, arguments );
7018            }
7019        };
7020    }
7021
7022
7023    var cssPrefixes = [ "Webkit", "Moz", "ms" ],
7024        emptyStyle = document.createElement( "div" ).style,
7025        vendorProps = {};
7026
7027// Return a vendor-prefixed property or undefined
7028    function vendorPropName( name ) {
7029
7030        // Check for vendor prefixed names
7031        var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
7032            i = cssPrefixes.length;
7033
7034        while ( i-- ) {
7035            name = cssPrefixes[ i ] + capName;
7036            if ( name in emptyStyle ) {
7037                return name;
7038            }
7039        }
7040    }
7041
7042// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
7043    function finalPropName( name ) {
7044        var final = jQuery.cssProps[ name ] || vendorProps[ name ];
7045
7046        if ( final ) {
7047            return final;
7048        }
7049        if ( name in emptyStyle ) {
7050            return name;
7051        }
7052        return vendorProps[ name ] = vendorPropName( name ) || name;
7053    }
7054
7055
7056    var
7057
7058        // Swappable if display is none or starts with table
7059        // except "table", "table-cell", or "table-caption"
7060        // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
7061        rdisplayswap = /^(none|table(?!-c[ea]).+)/,
7062        rcustomProp = /^--/,
7063        cssShow = { position: "absolute", visibility: "hidden", display: "block" },
7064        cssNormalTransform = {
7065            letterSpacing: "0",
7066            fontWeight: "400"
7067        };
7068
7069    function setPositiveNumber( _elem, value, subtract ) {
7070
7071        // Any relative (+/-) values have already been
7072        // normalized at this point
7073        var matches = rcssNum.exec( value );
7074        return matches ?
7075
7076            // Guard against undefined "subtract", e.g., when used as in cssHooks
7077            Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
7078            value;
7079    }
7080
7081    function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
7082        var i = dimension === "width" ? 1 : 0,
7083            extra = 0,
7084            delta = 0;
7085
7086        // Adjustment may not be necessary
7087        if ( box === ( isBorderBox ? "border" : "content" ) ) {
7088            return 0;
7089        }
7090
7091        for ( ; i < 4; i += 2 ) {
7092
7093            // Both box models exclude margin
7094            if ( box === "margin" ) {
7095                delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
7096            }
7097
7098            // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
7099            if ( !isBorderBox ) {
7100
7101                // Add padding
7102                delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7103
7104                // For "border" or "margin", add border
7105                if ( box !== "padding" ) {
7106                    delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7107
7108                    // But still keep track of it otherwise
7109                } else {
7110                    extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7111                }
7112
7113                // If we get here with a border-box (content + padding + border), we're seeking "content" or
7114                // "padding" or "margin"
7115            } else {
7116
7117                // For "content", subtract padding
7118                if ( box === "content" ) {
7119                    delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7120                }
7121
7122                // For "content" or "padding", subtract border
7123                if ( box !== "margin" ) {
7124                    delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7125                }
7126            }
7127        }
7128
7129        // Account for positive content-box scroll gutter when requested by providing computedVal
7130        if ( !isBorderBox && computedVal >= 0 ) {
7131
7132            // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
7133            // Assuming integer scroll gutter, subtract the rest and round down
7134            delta += Math.max( 0, Math.ceil(
7135                elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
7136                computedVal -
7137                delta -
7138                extra -
7139                0.5
7140
7141                // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
7142                // Use an explicit zero to avoid NaN (gh-3964)
7143            ) ) || 0;
7144        }
7145
7146        return delta;
7147    }
7148
7149    function getWidthOrHeight( elem, dimension, extra ) {
7150
7151        // Start with computed style
7152        var styles = getStyles( elem ),
7153
7154            // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
7155            // Fake content-box until we know it's needed to know the true value.
7156            boxSizingNeeded = !support.boxSizingReliable() || extra,
7157            isBorderBox = boxSizingNeeded &&
7158                jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7159            valueIsBorderBox = isBorderBox,
7160
7161            val = curCSS( elem, dimension, styles ),
7162            offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
7163
vendor: 6,163 bytes, lines 7164-7323
7164        // Support: Firefox <=54
7165        // Return a confounding non-pixel value or feign ignorance, as appropriate.
7166        if ( rnumnonpx.test( val ) ) {
7167            if ( !extra ) {
7168                return val;
7169            }
7170            val = "auto";
7171        }
7172
7173
7174        // Support: IE 9 - 11 only
7175        // Use offsetWidth/offsetHeight for when box sizing is unreliable.
7176        // In those cases, the computed value can be trusted to be border-box.
7177        if ( ( !support.boxSizingReliable() && isBorderBox ||
7178
7179            // Support: IE 10 - 11+, Edge 15 - 18+
7180            // IE/Edge misreport `getComputedStyle` of table rows with width/height
7181            // set in CSS while `offset*` properties report correct values.
7182            // Interestingly, in some cases IE 9 doesn't suffer from this issue.
7183            !support.reliableTrDimensions() && nodeName( elem, "tr" ) ||
7184
7185            // Fall back to offsetWidth/offsetHeight when value is "auto"
7186            // This happens for inline elements with no explicit setting (gh-3571)
7187            val === "auto" ||
7188
7189            // Support: Android <=4.1 - 4.3 only
7190            // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
7191            !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
7192
7193            // Make sure the element is visible & connected
7194            elem.getClientRects().length ) {
7195
7196            isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
7197
7198            // Where available, offsetWidth/offsetHeight approximate border box dimensions.
7199            // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
7200            // retrieved value as a content box dimension.
7201            valueIsBorderBox = offsetProp in elem;
7202            if ( valueIsBorderBox ) {
7203                val = elem[ offsetProp ];
7204            }
7205        }
7206
7207        // Normalize "" and auto
7208        val = parseFloat( val ) || 0;
7209
7210        // Adjust for the element's box model
7211        return ( val +
7212            boxModelAdjustment(
7213                elem,
7214                dimension,
7215                extra || ( isBorderBox ? "border" : "content" ),
7216                valueIsBorderBox,
7217                styles,
7218
7219                // Provide the current computed size to request scroll gutter calculation (gh-3589)
7220                val
7221            )
7222        ) + "px";
7223    }
7224
7225    jQuery.extend( {
7226
7227        // Add in style property hooks for overriding the default
7228        // behavior of getting and setting a style property
7229        cssHooks: {
7230            opacity: {
7231                get: function( elem, computed ) {
7232                    if ( computed ) {
7233
7234                        // We should always get a number back from opacity
7235                        var ret = curCSS( elem, "opacity" );
7236                        return ret === "" ? "1" : ret;
7237                    }
7238                }
7239            }
7240        },
7241
7242        // Don't automatically add "px" to these possibly-unitless properties
7243        cssNumber: {
7244            "animationIterationCount": true,
7245            "columnCount": true,
7246            "fillOpacity": true,
7247            "flexGrow": true,
7248            "flexShrink": true,
7249            "fontWeight": true,
7250            "gridArea": true,
7251            "gridColumn": true,
7252            "gridColumnEnd": true,
7253            "gridColumnStart": true,
7254            "gridRow": true,
7255            "gridRowEnd": true,
7256            "gridRowStart": true,
7257            "lineHeight": true,
7258            "opacity": true,
7259            "order": true,
7260            "orphans": true,
7261            "widows": true,
7262            "zIndex": true,
7263            "zoom": true
7264        },
7265
7266        // Add in properties whose names you wish to fix before
7267        // setting or getting the value
7268        cssProps: {},
7269
7270        // Get and set the style property on a DOM Node
7271        style: function( elem, name, value, extra ) {
7272
7273            // Don't set styles on text and comment nodes
7274            if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
7275                return;
7276            }
7277
7278            // Make sure that we're working with the right name
7279            var ret, type, hooks,
7280                origName = camelCase( name ),
7281                isCustomProp = rcustomProp.test( name ),
7282                style = elem.style;
7283
7284            // Make sure that we're working with the right name. We don't
7285            // want to query the value if it is a CSS custom property
7286            // since they are user-defined.
7287            if ( !isCustomProp ) {
7288                name = finalPropName( origName );
7289            }
7290
7291            // Gets hook for the prefixed version, then unprefixed version
7292            hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
7293
7294            // Check if we're setting a value
7295            if ( value !== undefined ) {
7296                type = typeof value;
7297
7298                // Convert "+=" or "-=" to relative numbers (#7345)
7299                if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
7300                    value = adjustCSS( elem, name, ret );
7301
7302                    // Fixes bug #9237
7303                    type = "number";
7304                }
7305
7306                // Make sure that null and NaN values aren't set (#7116)
7307                if ( value == null || value !== value ) {
7308                    return;
7309                }
7310
7311                // If a number was passed in, add the unit (except for certain CSS properties)
7312                // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
7313                // "px" to a few hardcoded values.
7314                if ( type === "number" && !isCustomProp ) {
7315                    value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
7316                }
7317
7318                // background-* props affect original clone's values
7319                if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
7320                    style[ name ] = "inherit";
7321                }
7322
7323                // If a hook was provided, use that value, otherwise just set the specified value
vendor: 15,960 bytes, lines 7324-7768
7324                if ( !hooks || !( "set" in hooks ) ||
7325                    ( value = hooks.set( elem, value, extra ) ) !== undefined ) {
7326
7327                    if ( isCustomProp ) {
7328                        style.setProperty( name, value );
7329                    } else {
7330                        style[ name ] = value;
7331                    }
7332                }
7333
7334            } else {
7335
7336                // If a hook was provided get the non-computed value from there
7337                if ( hooks && "get" in hooks &&
7338                    ( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
7339
7340                    return ret;
7341                }
7342
7343                // Otherwise just get the value from the style object
7344                return style[ name ];
7345            }
7346        },
7347
7348        css: function( elem, name, extra, styles ) {
7349            var val, num, hooks,
7350                origName = camelCase( name ),
7351                isCustomProp = rcustomProp.test( name );
7352
7353            // Make sure that we're working with the right name. We don't
7354            // want to modify the value if it is a CSS custom property
7355            // since they are user-defined.
7356            if ( !isCustomProp ) {
7357                name = finalPropName( origName );
7358            }
7359
7360            // Try prefixed name followed by the unprefixed name
7361            hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
7362
7363            // If a hook was provided get the computed value from there
7364            if ( hooks && "get" in hooks ) {
7365                val = hooks.get( elem, true, extra );
7366            }
7367
7368            // Otherwise, if a way to get the computed value exists, use that
7369            if ( val === undefined ) {
7370                val = curCSS( elem, name, styles );
7371            }
7372
7373            // Convert "normal" to computed value
7374            if ( val === "normal" && name in cssNormalTransform ) {
7375                val = cssNormalTransform[ name ];
7376            }
7377
7378            // Make numeric if forced or a qualifier was provided and val looks numeric
7379            if ( extra === "" || extra ) {
7380                num = parseFloat( val );
7381                return extra === true || isFinite( num ) ? num || 0 : val;
7382            }
7383
7384            return val;
7385        }
7386    } );
7387
7388    jQuery.each( [ "height", "width" ], function( _i, dimension ) {
7389        jQuery.cssHooks[ dimension ] = {
7390            get: function( elem, computed, extra ) {
7391                if ( computed ) {
7392
7393                    // Certain elements can have dimension info if we invisibly show them
7394                    // but it must have a current display style that would benefit
7395                    return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
7396
7397                    // Support: Safari 8+
7398                    // Table columns in Safari have non-zero offsetWidth & zero
7399                    // getBoundingClientRect().width unless display is changed.
7400                    // Support: IE <=11 only
7401                    // Running getBoundingClientRect on a disconnected node
7402                    // in IE throws an error.
7403                    ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
7404                        swap( elem, cssShow, function() {
7405                            return getWidthOrHeight( elem, dimension, extra );
7406                        } ) :
7407                        getWidthOrHeight( elem, dimension, extra );
7408                }
7409            },
7410
7411            set: function( elem, value, extra ) {
7412                var matches,
7413                    styles = getStyles( elem ),
7414
7415                    // Only read styles.position if the test has a chance to fail
7416                    // to avoid forcing a reflow.
7417                    scrollboxSizeBuggy = !support.scrollboxSize() &&
7418                        styles.position === "absolute",
7419
7420                    // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
7421                    boxSizingNeeded = scrollboxSizeBuggy || extra,
7422                    isBorderBox = boxSizingNeeded &&
7423                        jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7424                    subtract = extra ?
7425                        boxModelAdjustment(
7426                            elem,
7427                            dimension,
7428                            extra,
7429                            isBorderBox,
7430                            styles
7431                        ) :
7432                        0;
7433
7434                // Account for unreliable border-box dimensions by comparing offset* to computed and
7435                // faking a content-box to get border and padding (gh-3699)
7436                if ( isBorderBox && scrollboxSizeBuggy ) {
7437                    subtract -= Math.ceil(
7438                        elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
7439                        parseFloat( styles[ dimension ] ) -
7440                        boxModelAdjustment( elem, dimension, "border", false, styles ) -
7441                        0.5
7442                    );
7443                }
7444
7445                // Convert to pixels if value adjustment is needed
7446                if ( subtract && ( matches = rcssNum.exec( value ) ) &&
7447                    ( matches[ 3 ] || "px" ) !== "px" ) {
7448
7449                    elem.style[ dimension ] = value;
7450                    value = jQuery.css( elem, dimension );
7451                }
7452
7453                return setPositiveNumber( elem, value, subtract );
7454            }
7455        };
7456    } );
7457
7458    jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
7459        function( elem, computed ) {
7460            if ( computed ) {
7461                return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
7462                    elem.getBoundingClientRect().left -
7463                    swap( elem, { marginLeft: 0 }, function() {
7464                        return elem.getBoundingClientRect().left;
7465                    } )
7466                ) + "px";
7467            }
7468        }
7469    );
7470
7471// These hooks are used by animate to expand properties
7472    jQuery.each( {
7473        margin: "",
7474        padding: "",
7475        border: "Width"
7476    }, function( prefix, suffix ) {
7477        jQuery.cssHooks[ prefix + suffix ] = {
7478            expand: function( value ) {
7479                var i = 0,
7480                    expanded = {},
7481
7482                    // Assumes a single number if not a string
7483                    parts = typeof value === "string" ? value.split( " " ) : [ value ];
7484
7485                for ( ; i < 4; i++ ) {
7486                    expanded[ prefix + cssExpand[ i ] + suffix ] =
7487                        parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7488                }
7489
7490                return expanded;
7491            }
7492        };
7493
7494        if ( prefix !== "margin" ) {
7495            jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7496        }
7497    } );
7498
7499    jQuery.fn.extend( {
7500        css: function( name, value ) {
7501            return access( this, function( elem, name, value ) {
7502                var styles, len,
7503                    map = {},
7504                    i = 0;
7505
7506                if ( Array.isArray( name ) ) {
7507                    styles = getStyles( elem );
7508                    len = name.length;
7509
7510                    for ( ; i < len; i++ ) {
7511                        map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
7512                    }
7513
7514                    return map;
7515                }
7516
7517                return value !== undefined ?
7518                    jQuery.style( elem, name, value ) :
7519                    jQuery.css( elem, name );
7520            }, name, value, arguments.length > 1 );
7521        }
7522    } );
7523
7524
7525    function Tween( elem, options, prop, end, easing ) {
7526        return new Tween.prototype.init( elem, options, prop, end, easing );
7527    }
7528    jQuery.Tween = Tween;
7529
7530    Tween.prototype = {
7531        constructor: Tween,
7532        init: function( elem, options, prop, end, easing, unit ) {
7533            this.elem = elem;
7534            this.prop = prop;
7535            this.easing = easing || jQuery.easing._default;
7536            this.options = options;
7537            this.start = this.now = this.cur();
7538            this.end = end;
7539            this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
7540        },
7541        cur: function() {
7542            var hooks = Tween.propHooks[ this.prop ];
7543
7544            return hooks && hooks.get ?
7545                hooks.get( this ) :
7546                Tween.propHooks._default.get( this );
7547        },
7548        run: function( percent ) {
7549            var eased,
7550                hooks = Tween.propHooks[ this.prop ];
7551
7552            if ( this.options.duration ) {
7553                this.pos = eased = jQuery.easing[ this.easing ](
7554                    percent, this.options.duration * percent, 0, 1, this.options.duration
7555                );
7556            } else {
7557                this.pos = eased = percent;
7558            }
7559            this.now = ( this.end - this.start ) * eased + this.start;
7560
7561            if ( this.options.step ) {
7562                this.options.step.call( this.elem, this.now, this );
7563            }
7564
7565            if ( hooks && hooks.set ) {
7566                hooks.set( this );
7567            } else {
7568                Tween.propHooks._default.set( this );
7569            }
7570            return this;
7571        }
7572    };
7573
7574    Tween.prototype.init.prototype = Tween.prototype;
7575
7576    Tween.propHooks = {
7577        _default: {
7578            get: function( tween ) {
7579                var result;
7580
7581                // Use a property on the element directly when it is not a DOM element,
7582                // or when there is no matching style property that exists.
7583                if ( tween.elem.nodeType !== 1 ||
7584                    tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
7585                    return tween.elem[ tween.prop ];
7586                }
7587
7588                // Passing an empty string as a 3rd parameter to .css will automatically
7589                // attempt a parseFloat and fallback to a string if the parse fails.
7590                // Simple values such as "10px" are parsed to Float;
7591                // complex values such as "rotate(1rad)" are returned as-is.
7592                result = jQuery.css( tween.elem, tween.prop, "" );
7593
7594                // Empty strings, null, undefined and "auto" are converted to 0.
7595                return !result || result === "auto" ? 0 : result;
7596            },
7597            set: function( tween ) {
7598
7599                // Use step hook for back compat.
7600                // Use cssHook if its there.
7601                // Use .style if available and use plain properties where available.
7602                if ( jQuery.fx.step[ tween.prop ] ) {
7603                    jQuery.fx.step[ tween.prop ]( tween );
7604                } else if ( tween.elem.nodeType === 1 && (
7605                    jQuery.cssHooks[ tween.prop ] ||
7606                    tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
7607                    jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
7608                } else {
7609                    tween.elem[ tween.prop ] = tween.now;
7610                }
7611            }
7612        }
7613    };
7614
7615// Support: IE <=9 only
7616// Panic based approach to setting things on disconnected nodes
7617    Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
7618        set: function( tween ) {
7619            if ( tween.elem.nodeType && tween.elem.parentNode ) {
7620                tween.elem[ tween.prop ] = tween.now;
7621            }
7622        }
7623    };
7624
7625    jQuery.easing = {
7626        linear: function( p ) {
7627            return p;
7628        },
7629        swing: function( p ) {
7630            return 0.5 - Math.cos( p * Math.PI ) / 2;
7631        },
7632        _default: "swing"
7633    };
7634
7635    jQuery.fx = Tween.prototype.init;
7636
7637// Back compat <1.8 extension point
7638    jQuery.fx.step = {};
7639
7640
7641
7642
7643    var
7644        fxNow, inProgress,
7645        rfxtypes = /^(?:toggle|show|hide)$/,
7646        rrun = /queueHooks$/;
7647
7648    function schedule() {
7649        if ( inProgress ) {
7650            if ( document.hidden === false && window.requestAnimationFrame ) {
7651                window.requestAnimationFrame( schedule );
7652            } else {
7653                window.setTimeout( schedule, jQuery.fx.interval );
7654            }
7655
7656            jQuery.fx.tick();
7657        }
7658    }
7659
7660// Animations created synchronously will run synchronously
7661    function createFxNow() {
7662        window.setTimeout( function() {
7663            fxNow = undefined;
7664        } );
7665        return ( fxNow = Date.now() );
7666    }
7667
7668// Generate parameters to create a standard animation
7669    function genFx( type, includeWidth ) {
7670        var which,
7671            i = 0,
7672            attrs = { height: type };
7673
7674        // If we include width, step value is 1 to do all cssExpand values,
7675        // otherwise step value is 2 to skip over Left and Right
7676        includeWidth = includeWidth ? 1 : 0;
7677        for ( ; i < 4; i += 2 - includeWidth ) {
7678            which = cssExpand[ i ];
7679            attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
7680        }
7681
7682        if ( includeWidth ) {
7683            attrs.opacity = attrs.width = type;
7684        }
7685
7686        return attrs;
7687    }
7688
7689    function createTween( value, prop, animation ) {
7690        var tween,
7691            collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
7692            index = 0,
7693            length = collection.length;
7694        for ( ; index < length; index++ ) {
7695            if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
7696
7697                // We're done with this property
7698                return tween;
7699            }
7700        }
7701    }
7702
7703    function defaultPrefilter( elem, props, opts ) {
7704        var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
7705            isBox = "width" in props || "height" in props,
7706            anim = this,
7707            orig = {},
7708            style = elem.style,
7709            hidden = elem.nodeType && isHiddenWithinTree( elem ),
7710            dataShow = dataPriv.get( elem, "fxshow" );
7711
7712        // Queue-skipping animations hijack the fx hooks
7713        if ( !opts.queue ) {
7714            hooks = jQuery._queueHooks( elem, "fx" );
7715            if ( hooks.unqueued == null ) {
7716                hooks.unqueued = 0;
7717                oldfire = hooks.empty.fire;
7718                hooks.empty.fire = function() {
7719                    if ( !hooks.unqueued ) {
7720                        oldfire();
7721                    }
7722                };
7723            }
7724            hooks.unqueued++;
7725
7726            anim.always( function() {
7727
7728                // Ensure the complete handler is called before this completes
7729                anim.always( function() {
7730                    hooks.unqueued--;
7731                    if ( !jQuery.queue( elem, "fx" ).length ) {
7732                        hooks.empty.fire();
7733                    }
7734                } );
7735            } );
7736        }
7737
7738        // Detect show/hide animations
7739        for ( prop in props ) {
7740            value = props[ prop ];
7741            if ( rfxtypes.test( value ) ) {
7742                delete props[ prop ];
7743                toggle = toggle || value === "toggle";
7744                if ( value === ( hidden ? "hide" : "show" ) ) {
7745
7746                    // Pretend to be hidden if this is a "show" and
7747                    // there is still data from a stopped show/hide
7748                    if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
7749                        hidden = true;
7750
7751                        // Ignore all other no-op show/hide data
7752                    } else {
7753                        continue;
7754                    }
7755                }
7756                orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
7757            }
7758        }
7759
7760        // Bail out if this is a no-op like .hide().hide()
7761        propTween = !jQuery.isEmptyObject( props );
7762        if ( !propTween && jQuery.isEmptyObject( orig ) ) {
7763            return;
7764        }
7765
7766        // Restrict "overflow" and "display" styles during box animations
7767        if ( isBox && elem.nodeType === 1 ) {
7768
vendor: 5,766 bytes, lines 7769-7924
7769            // Support: IE <=9 - 11, Edge 12 - 15
7770            // Record all 3 overflow attributes because IE does not infer the shorthand
7771            // from identically-valued overflowX and overflowY and Edge just mirrors
7772            // the overflowX value there.
7773            opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
7774
7775            // Identify a display type, preferring old show/hide data over the CSS cascade
7776            restoreDisplay = dataShow && dataShow.display;
7777            if ( restoreDisplay == null ) {
7778                restoreDisplay = dataPriv.get( elem, "display" );
7779            }
7780            display = jQuery.css( elem, "display" );
7781            if ( display === "none" ) {
7782                if ( restoreDisplay ) {
7783                    display = restoreDisplay;
7784                } else {
7785
7786                    // Get nonempty value(s) by temporarily forcing visibility
7787                    showHide( [ elem ], true );
7788                    restoreDisplay = elem.style.display || restoreDisplay;
7789                    display = jQuery.css( elem, "display" );
7790                    showHide( [ elem ] );
7791                }
7792            }
7793
7794            // Animate inline elements as inline-block
7795            if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
7796                if ( jQuery.css( elem, "float" ) === "none" ) {
7797
7798                    // Restore the original display value at the end of pure show/hide animations
7799                    if ( !propTween ) {
7800                        anim.done( function() {
7801                            style.display = restoreDisplay;
7802                        } );
7803                        if ( restoreDisplay == null ) {
7804                            display = style.display;
7805                            restoreDisplay = display === "none" ? "" : display;
7806                        }
7807                    }
7808                    style.display = "inline-block";
7809                }
7810            }
7811        }
7812
7813        if ( opts.overflow ) {
7814            style.overflow = "hidden";
7815            anim.always( function() {
7816                style.overflow = opts.overflow[ 0 ];
7817                style.overflowX = opts.overflow[ 1 ];
7818                style.overflowY = opts.overflow[ 2 ];
7819            } );
7820        }
7821
7822        // Implement show/hide animations
7823        propTween = false;
7824        for ( prop in orig ) {
7825
7826            // General show/hide setup for this element animation
7827            if ( !propTween ) {
7828                if ( dataShow ) {
7829                    if ( "hidden" in dataShow ) {
7830                        hidden = dataShow.hidden;
7831                    }
7832                } else {
7833                    dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
7834                }
7835
7836                // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
7837                if ( toggle ) {
7838                    dataShow.hidden = !hidden;
7839                }
7840
7841                // Show elements before animating them
7842                if ( hidden ) {
7843                    showHide( [ elem ], true );
7844                }
7845
7846                /* eslint-disable no-loop-func */
7847
7848                anim.done( function() {
7849
7850                    /* eslint-enable no-loop-func */
7851
7852                    // The final step of a "hide" animation is actually hiding the element
7853                    if ( !hidden ) {
7854                        showHide( [ elem ] );
7855                    }
7856                    dataPriv.remove( elem, "fxshow" );
7857                    for ( prop in orig ) {
7858                        jQuery.style( elem, prop, orig[ prop ] );
7859                    }
7860                } );
7861            }
7862
7863            // Per-property setup
7864            propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
7865            if ( !( prop in dataShow ) ) {
7866                dataShow[ prop ] = propTween.start;
7867                if ( hidden ) {
7868                    propTween.end = propTween.start;
7869                    propTween.start = 0;
7870                }
7871            }
7872        }
7873    }
7874
7875    function propFilter( props, specialEasing ) {
7876        var index, name, easing, value, hooks;
7877
7878        // camelCase, specialEasing and expand cssHook pass
7879        for ( index in props ) {
7880            name = camelCase( index );
7881            easing = specialEasing[ name ];
7882            value = props[ index ];
7883            if ( Array.isArray( value ) ) {
7884                easing = value[ 1 ];
7885                value = props[ index ] = value[ 0 ];
7886            }
7887
7888            if ( index !== name ) {
7889                props[ name ] = value;
7890                delete props[ index ];
7891            }
7892
7893            hooks = jQuery.cssHooks[ name ];
7894            if ( hooks && "expand" in hooks ) {
7895                value = hooks.expand( value );
7896                delete props[ name ];
7897
7898                // Not quite $.extend, this won't overwrite existing keys.
7899                // Reusing 'index' because we have the correct "name"
7900                for ( index in value ) {
7901                    if ( !( index in props ) ) {
7902                        props[ index ] = value[ index ];
7903                        specialEasing[ index ] = easing;
7904                    }
7905                }
7906            } else {
7907                specialEasing[ name ] = easing;
7908            }
7909        }
7910    }
7911
7912    function Animation( elem, properties, options ) {
7913        var result,
7914            stopped,
7915            index = 0,
7916            length = Animation.prefilters.length,
7917            deferred = jQuery.Deferred().always( function() {
7918
7919                // Don't match elem in the :animated selector
7920                delete tick.elem;
7921            } ),
7922            tick = function() {
7923                if ( stopped ) {
7924                    return false;
vendor: 20,456 bytes, lines 7925-8541
7925                }
7926                var currentTime = fxNow || createFxNow(),
7927                    remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
7928
7929                    // Support: Android 2.3 only
7930                    // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
7931                    temp = remaining / animation.duration || 0,
7932                    percent = 1 - temp,
7933                    index = 0,
7934                    length = animation.tweens.length;
7935
7936                for ( ; index < length; index++ ) {
7937                    animation.tweens[ index ].run( percent );
7938                }
7939
7940                deferred.notifyWith( elem, [ animation, percent, remaining ] );
7941
7942                // If there's more to do, yield
7943                if ( percent < 1 && length ) {
7944                    return remaining;
7945                }
7946
7947                // If this was an empty animation, synthesize a final progress notification
7948                if ( !length ) {
7949                    deferred.notifyWith( elem, [ animation, 1, 0 ] );
7950                }
7951
7952                // Resolve the animation and report its conclusion
7953                deferred.resolveWith( elem, [ animation ] );
7954                return false;
7955            },
7956            animation = deferred.promise( {
7957                elem: elem,
7958                props: jQuery.extend( {}, properties ),
7959                opts: jQuery.extend( true, {
7960                    specialEasing: {},
7961                    easing: jQuery.easing._default
7962                }, options ),
7963                originalProperties: properties,
7964                originalOptions: options,
7965                startTime: fxNow || createFxNow(),
7966                duration: options.duration,
7967                tweens: [],
7968                createTween: function( prop, end ) {
7969                    var tween = jQuery.Tween( elem, animation.opts, prop, end,
7970                        animation.opts.specialEasing[ prop ] || animation.opts.easing );
7971                    animation.tweens.push( tween );
7972                    return tween;
7973                },
7974                stop: function( gotoEnd ) {
7975                    var index = 0,
7976
7977                        // If we are going to the end, we want to run all the tweens
7978                        // otherwise we skip this part
7979                        length = gotoEnd ? animation.tweens.length : 0;
7980                    if ( stopped ) {
7981                        return this;
7982                    }
7983                    stopped = true;
7984                    for ( ; index < length; index++ ) {
7985                        animation.tweens[ index ].run( 1 );
7986                    }
7987
7988                    // Resolve when we played the last frame; otherwise, reject
7989                    if ( gotoEnd ) {
7990                        deferred.notifyWith( elem, [ animation, 1, 0 ] );
7991                        deferred.resolveWith( elem, [ animation, gotoEnd ] );
7992                    } else {
7993                        deferred.rejectWith( elem, [ animation, gotoEnd ] );
7994                    }
7995                    return this;
7996                }
7997            } ),
7998            props = animation.props;
7999
8000        propFilter( props, animation.opts.specialEasing );
8001
8002        for ( ; index < length; index++ ) {
8003            result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
8004            if ( result ) {
8005                if ( isFunction( result.stop ) ) {
8006                    jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
8007                        result.stop.bind( result );
8008                }
8009                return result;
8010            }
8011        }
8012
8013        jQuery.map( props, createTween, animation );
8014
8015        if ( isFunction( animation.opts.start ) ) {
8016            animation.opts.start.call( elem, animation );
8017        }
8018
8019        // Attach callbacks from options
8020        animation
8021            .progress( animation.opts.progress )
8022            .done( animation.opts.done, animation.opts.complete )
8023            .fail( animation.opts.fail )
8024            .always( animation.opts.always );
8025
8026        jQuery.fx.timer(
8027            jQuery.extend( tick, {
8028                elem: elem,
8029                anim: animation,
8030                queue: animation.opts.queue
8031            } )
8032        );
8033
8034        return animation;
8035    }
8036
8037    jQuery.Animation = jQuery.extend( Animation, {
8038
8039        tweeners: {
8040            "*": [ function( prop, value ) {
8041                var tween = this.createTween( prop, value );
8042                adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
8043                return tween;
8044            } ]
8045        },
8046
8047        tweener: function( props, callback ) {
8048            if ( isFunction( props ) ) {
8049                callback = props;
8050                props = [ "*" ];
8051            } else {
8052                props = props.match( rnothtmlwhite );
8053            }
8054
8055            var prop,
8056                index = 0,
8057                length = props.length;
8058
8059            for ( ; index < length; index++ ) {
8060                prop = props[ index ];
8061                Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
8062                Animation.tweeners[ prop ].unshift( callback );
8063            }
8064        },
8065
8066        prefilters: [ defaultPrefilter ],
8067
8068        prefilter: function( callback, prepend ) {
8069            if ( prepend ) {
8070                Animation.prefilters.unshift( callback );
8071            } else {
8072                Animation.prefilters.push( callback );
8073            }
8074        }
8075    } );
8076
8077    jQuery.speed = function( speed, easing, fn ) {
8078        var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
8079            complete: fn || !fn && easing ||
8080                isFunction( speed ) && speed,
8081            duration: speed,
8082            easing: fn && easing || easing && !isFunction( easing ) && easing
8083        };
8084
8085        // Go to the end state if fx are off
8086        if ( jQuery.fx.off ) {
8087            opt.duration = 0;
8088
8089        } else {
8090            if ( typeof opt.duration !== "number" ) {
8091                if ( opt.duration in jQuery.fx.speeds ) {
8092                    opt.duration = jQuery.fx.speeds[ opt.duration ];
8093
8094                } else {
8095                    opt.duration = jQuery.fx.speeds._default;
8096                }
8097            }
8098        }
8099
8100        // Normalize opt.queue - true/undefined/null -> "fx"
8101        if ( opt.queue == null || opt.queue === true ) {
8102            opt.queue = "fx";
8103        }
8104
8105        // Queueing
8106        opt.old = opt.complete;
8107
8108        opt.complete = function() {
8109            if ( isFunction( opt.old ) ) {
8110                opt.old.call( this );
8111            }
8112
8113            if ( opt.queue ) {
8114                jQuery.dequeue( this, opt.queue );
8115            }
8116        };
8117
8118        return opt;
8119    };
8120
8121    jQuery.fn.extend( {
8122        fadeTo: function( speed, to, easing, callback ) {
8123
8124            // Show any hidden elements after setting opacity to 0
8125            return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
8126
8127            // Animate to the value specified
8128                .end().animate( { opacity: to }, speed, easing, callback );
8129        },
8130        animate: function( prop, speed, easing, callback ) {
8131            var empty = jQuery.isEmptyObject( prop ),
8132                optall = jQuery.speed( speed, easing, callback ),
8133                doAnimation = function() {
8134
8135                    // Operate on a copy of prop so per-property easing won't be lost
8136                    var anim = Animation( this, jQuery.extend( {}, prop ), optall );
8137
8138                    // Empty animations, or finishing resolves immediately
8139                    if ( empty || dataPriv.get( this, "finish" ) ) {
8140                        anim.stop( true );
8141                    }
8142                };
8143
8144            doAnimation.finish = doAnimation;
8145
8146            return empty || optall.queue === false ?
8147                this.each( doAnimation ) :
8148                this.queue( optall.queue, doAnimation );
8149        },
8150        stop: function( type, clearQueue, gotoEnd ) {
8151            var stopQueue = function( hooks ) {
8152                var stop = hooks.stop;
8153                delete hooks.stop;
8154                stop( gotoEnd );
8155            };
8156
8157            if ( typeof type !== "string" ) {
8158                gotoEnd = clearQueue;
8159                clearQueue = type;
8160                type = undefined;
8161            }
8162            if ( clearQueue ) {
8163                this.queue( type || "fx", [] );
8164            }
8165
8166            return this.each( function() {
8167                var dequeue = true,
8168                    index = type != null && type + "queueHooks",
8169                    timers = jQuery.timers,
8170                    data = dataPriv.get( this );
8171
8172                if ( index ) {
8173                    if ( data[ index ] && data[ index ].stop ) {
8174                        stopQueue( data[ index ] );
8175                    }
8176                } else {
8177                    for ( index in data ) {
8178                        if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
8179                            stopQueue( data[ index ] );
8180                        }
8181                    }
8182                }
8183
8184                for ( index = timers.length; index--; ) {
8185                    if ( timers[ index ].elem === this &&
8186                        ( type == null || timers[ index ].queue === type ) ) {
8187
8188                        timers[ index ].anim.stop( gotoEnd );
8189                        dequeue = false;
8190                        timers.splice( index, 1 );
8191                    }
8192                }
8193
8194                // Start the next in the queue if the last step wasn't forced.
8195                // Timers currently will call their complete callbacks, which
8196                // will dequeue but only if they were gotoEnd.
8197                if ( dequeue || !gotoEnd ) {
8198                    jQuery.dequeue( this, type );
8199                }
8200            } );
8201        },
8202        finish: function( type ) {
8203            if ( type !== false ) {
8204                type = type || "fx";
8205            }
8206            return this.each( function() {
8207                var index,
8208                    data = dataPriv.get( this ),
8209                    queue = data[ type + "queue" ],
8210                    hooks = data[ type + "queueHooks" ],
8211                    timers = jQuery.timers,
8212                    length = queue ? queue.length : 0;
8213
8214                // Enable finishing flag on private data
8215                data.finish = true;
8216
8217                // Empty the queue first
8218                jQuery.queue( this, type, [] );
8219
8220                if ( hooks && hooks.stop ) {
8221                    hooks.stop.call( this, true );
8222                }
8223
8224                // Look for any active animations, and finish them
8225                for ( index = timers.length; index--; ) {
8226                    if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
8227                        timers[ index ].anim.stop( true );
8228                        timers.splice( index, 1 );
8229                    }
8230                }
8231
8232                // Look for any animations in the old queue and finish them
8233                for ( index = 0; index < length; index++ ) {
8234                    if ( queue[ index ] && queue[ index ].finish ) {
8235                        queue[ index ].finish.call( this );
8236                    }
8237                }
8238
8239                // Turn off finishing flag
8240                delete data.finish;
8241            } );
8242        }
8243    } );
8244
8245    jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
8246        var cssFn = jQuery.fn[ name ];
8247        jQuery.fn[ name ] = function( speed, easing, callback ) {
8248            return speed == null || typeof speed === "boolean" ?
8249                cssFn.apply( this, arguments ) :
8250                this.animate( genFx( name, true ), speed, easing, callback );
8251        };
8252    } );
8253
8254// Generate shortcuts for custom animations
8255    jQuery.each( {
8256        slideDown: genFx( "show" ),
8257        slideUp: genFx( "hide" ),
8258        slideToggle: genFx( "toggle" ),
8259        fadeIn: { opacity: "show" },
8260        fadeOut: { opacity: "hide" },
8261        fadeToggle: { opacity: "toggle" }
8262    }, function( name, props ) {
8263        jQuery.fn[ name ] = function( speed, easing, callback ) {
8264            return this.animate( props, speed, easing, callback );
8265        };
8266    } );
8267
8268    jQuery.timers = [];
8269    jQuery.fx.tick = function() {
8270        var timer,
8271            i = 0,
8272            timers = jQuery.timers;
8273
8274        fxNow = Date.now();
8275
8276        for ( ; i < timers.length; i++ ) {
8277            timer = timers[ i ];
8278
8279            // Run the timer and safely remove it when done (allowing for external removal)
8280            if ( !timer() && timers[ i ] === timer ) {
8281                timers.splice( i--, 1 );
8282            }
8283        }
8284
8285        if ( !timers.length ) {
8286            jQuery.fx.stop();
8287        }
8288        fxNow = undefined;
8289    };
8290
8291    jQuery.fx.timer = function( timer ) {
8292        jQuery.timers.push( timer );
8293        jQuery.fx.start();
8294    };
8295
8296    jQuery.fx.interval = 13;
8297    jQuery.fx.start = function() {
8298        if ( inProgress ) {
8299            return;
8300        }
8301
8302        inProgress = true;
8303        schedule();
8304    };
8305
8306    jQuery.fx.stop = function() {
8307        inProgress = null;
8308    };
8309
8310    jQuery.fx.speeds = {
8311        slow: 600,
8312        fast: 200,
8313
8314        // Default speed
8315        _default: 400
8316    };
8317
8318
8319// Based off of the plugin by Clint Helfers, with permission.
8320// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
8321    jQuery.fn.delay = function( time, type ) {
8322        time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
8323        type = type || "fx";
8324
8325        return this.queue( type, function( next, hooks ) {
8326            var timeout = window.setTimeout( next, time );
8327            hooks.stop = function() {
8328                window.clearTimeout( timeout );
8329            };
8330        } );
8331    };
8332
8333
8334    ( function() {
8335        var input = document.createElement( "input" ),
8336            select = document.createElement( "select" ),
8337            opt = select.appendChild( document.createElement( "option" ) );
8338
8339        input.type = "checkbox";
8340
8341        // Support: Android <=4.3 only
8342        // Default value for a checkbox should be "on"
8343        support.checkOn = input.value !== "";
8344
8345        // Support: IE <=11 only
8346        // Must access selectedIndex to make default options select
8347        support.optSelected = opt.selected;
8348
8349        // Support: IE <=11 only
8350        // An input loses its value after becoming a radio
8351        input = document.createElement( "input" );
8352        input.value = "t";
8353        input.type = "radio";
8354        support.radioValue = input.value === "t";
8355    } )();
8356
8357
8358    var boolHook,
8359        attrHandle = jQuery.expr.attrHandle;
8360
8361    jQuery.fn.extend( {
8362        attr: function( name, value ) {
8363            return access( this, jQuery.attr, name, value, arguments.length > 1 );
8364        },
8365
8366        removeAttr: function( name ) {
8367            return this.each( function() {
8368                jQuery.removeAttr( this, name );
8369            } );
8370        }
8371    } );
8372
8373    jQuery.extend( {
8374        attr: function( elem, name, value ) {
8375            var ret, hooks,
8376                nType = elem.nodeType;
8377
8378            // Don't get/set attributes on text, comment and attribute nodes
8379            if ( nType === 3 || nType === 8 || nType === 2 ) {
8380                return;
8381            }
8382
8383            // Fallback to prop when attributes are not supported
8384            if ( typeof elem.getAttribute === "undefined" ) {
8385                return jQuery.prop( elem, name, value );
8386            }
8387
8388            // Attribute hooks are determined by the lowercase version
8389            // Grab necessary hook if one is defined
8390            if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
8391                hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
8392                    ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
8393            }
8394
8395            if ( value !== undefined ) {
8396                if ( value === null ) {
8397                    jQuery.removeAttr( elem, name );
8398                    return;
8399                }
8400
8401                if ( hooks && "set" in hooks &&
8402                    ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
8403                    return ret;
8404                }
8405
8406                elem.setAttribute( name, value + "" );
8407                return value;
8408            }
8409
8410            if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
8411                return ret;
8412            }
8413
8414            ret = jQuery.find.attr( elem, name );
8415
8416            // Non-existent attributes return null, we normalize to undefined
8417            return ret == null ? undefined : ret;
8418        },
8419
8420        attrHooks: {
8421            type: {
8422                set: function( elem, value ) {
8423                    if ( !support.radioValue && value === "radio" &&
8424                        nodeName( elem, "input" ) ) {
8425                        var val = elem.value;
8426                        elem.setAttribute( "type", value );
8427                        if ( val ) {
8428                            elem.value = val;
8429                        }
8430                        return value;
8431                    }
8432                }
8433            }
8434        },
8435
8436        removeAttr: function( elem, value ) {
8437            var name,
8438                i = 0,
8439
8440                // Attribute names can contain non-HTML whitespace characters
8441                // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
8442                attrNames = value && value.match( rnothtmlwhite );
8443
8444            if ( attrNames && elem.nodeType === 1 ) {
8445                while ( ( name = attrNames[ i++ ] ) ) {
8446                    elem.removeAttribute( name );
8447                }
8448            }
8449        }
8450    } );
8451
8452// Hooks for boolean attributes
8453    boolHook = {
8454        set: function( elem, value, name ) {
8455            if ( value === false ) {
8456
8457                // Remove boolean attributes when set to false
8458                jQuery.removeAttr( elem, name );
8459            } else {
8460                elem.setAttribute( name, name );
8461            }
8462            return name;
8463        }
8464    };
8465
8466    jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
8467        var getter = attrHandle[ name ] || jQuery.find.attr;
8468
8469        attrHandle[ name ] = function( elem, name, isXML ) {
8470            var ret, handle,
8471                lowercaseName = name.toLowerCase();
8472
8473            if ( !isXML ) {
8474
8475                // Avoid an infinite loop by temporarily removing this function from the getter
8476                handle = attrHandle[ lowercaseName ];
8477                attrHandle[ lowercaseName ] = ret;
8478                ret = getter( elem, name, isXML ) != null ?
8479                    lowercaseName :
8480                    null;
8481                attrHandle[ lowercaseName ] = handle;
8482            }
8483            return ret;
8484        };
8485    } );
8486
8487
8488
8489
8490    var rfocusable = /^(?:input|select|textarea|button)$/i,
8491        rclickable = /^(?:a|area)$/i;
8492
8493    jQuery.fn.extend( {
8494        prop: function( name, value ) {
8495            return access( this, jQuery.prop, name, value, arguments.length > 1 );
8496        },
8497
8498        removeProp: function( name ) {
8499            return this.each( function() {
8500                delete this[ jQuery.propFix[ name ] || name ];
8501            } );
8502        }
8503    } );
8504
8505    jQuery.extend( {
8506        prop: function( elem, name, value ) {
8507            var ret, hooks,
8508                nType = elem.nodeType;
8509
8510            // Don't get/set properties on text, comment and attribute nodes
8511            if ( nType === 3 || nType === 8 || nType === 2 ) {
8512                return;
8513            }
8514
8515            if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
8516
8517                // Fix name and attach hooks
8518                name = jQuery.propFix[ name ] || name;
8519                hooks = jQuery.propHooks[ name ];
8520            }
8521
8522            if ( value !== undefined ) {
8523                if ( hooks && "set" in hooks &&
8524                    ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
8525                    return ret;
8526                }
8527
8528                return ( elem[ name ] = value );
8529            }
8530
8531            if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
8532                return ret;
8533            }
8534
8535            return elem[ name ];
8536        },
8537
8538        propHooks: {
8539            tabIndex: {
8540                get: function( elem ) {
8541
vendor: 16,418 bytes, lines 8542-9030
8542                    // Support: IE <=9 - 11 only
8543                    // elem.tabIndex doesn't always return the
8544                    // correct value when it hasn't been explicitly set
8545                    // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
8546                    // Use proper attribute retrieval(#12072)
8547                    var tabindex = jQuery.find.attr( elem, "tabindex" );
8548
8549                    if ( tabindex ) {
8550                        return parseInt( tabindex, 10 );
8551                    }
8552
8553                    if (
8554                        rfocusable.test( elem.nodeName ) ||
8555                        rclickable.test( elem.nodeName ) &&
8556                        elem.href
8557                    ) {
8558                        return 0;
8559                    }
8560
8561                    return -1;
8562                }
8563            }
8564        },
8565
8566        propFix: {
8567            "for": "htmlFor",
8568            "class": "className"
8569        }
8570    } );
8571
8572// Support: IE <=11 only
8573// Accessing the selectedIndex property
8574// forces the browser to respect setting selected
8575// on the option
8576// The getter ensures a default option is selected
8577// when in an optgroup
8578// eslint rule "no-unused-expressions" is disabled for this code
8579// since it considers such accessions noop
8580    if ( !support.optSelected ) {
8581        jQuery.propHooks.selected = {
8582            get: function( elem ) {
8583
8584                /* eslint no-unused-expressions: "off" */
8585
8586                var parent = elem.parentNode;
8587                if ( parent && parent.parentNode ) {
8588                    parent.parentNode.selectedIndex;
8589                }
8590                return null;
8591            },
8592            set: function( elem ) {
8593
8594                /* eslint no-unused-expressions: "off" */
8595
8596                var parent = elem.parentNode;
8597                if ( parent ) {
8598                    parent.selectedIndex;
8599
8600                    if ( parent.parentNode ) {
8601                        parent.parentNode.selectedIndex;
8602                    }
8603                }
8604            }
8605        };
8606    }
8607
8608    jQuery.each( [
8609        "tabIndex",
8610        "readOnly",
8611        "maxLength",
8612        "cellSpacing",
8613        "cellPadding",
8614        "rowSpan",
8615        "colSpan",
8616        "useMap",
8617        "frameBorder",
8618        "contentEditable"
8619    ], function() {
8620        jQuery.propFix[ this.toLowerCase() ] = this;
8621    } );
8622
8623
8624
8625
8626    // Strip and collapse whitespace according to HTML spec
8627    // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
8628    function stripAndCollapse( value ) {
8629        var tokens = value.match( rnothtmlwhite ) || [];
8630        return tokens.join( " " );
8631    }
8632
8633
8634    function getClass( elem ) {
8635        return elem.getAttribute && elem.getAttribute( "class" ) || "";
8636    }
8637
8638    function classesToArray( value ) {
8639        if ( Array.isArray( value ) ) {
8640            return value;
8641        }
8642        if ( typeof value === "string" ) {
8643            return value.match( rnothtmlwhite ) || [];
8644        }
8645        return [];
8646    }
8647
8648    jQuery.fn.extend( {
8649        addClass: function( value ) {
8650            var classes, elem, cur, curValue, clazz, j, finalValue,
8651                i = 0;
8652
8653            if ( isFunction( value ) ) {
8654                return this.each( function( j ) {
8655                    jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
8656                } );
8657            }
8658
8659            classes = classesToArray( value );
8660
8661            if ( classes.length ) {
8662                while ( ( elem = this[ i++ ] ) ) {
8663                    curValue = getClass( elem );
8664                    cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
8665
8666                    if ( cur ) {
8667                        j = 0;
8668                        while ( ( clazz = classes[ j++ ] ) ) {
8669                            if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
8670                                cur += clazz + " ";
8671                            }
8672                        }
8673
8674                        // Only assign if different to avoid unneeded rendering.
8675                        finalValue = stripAndCollapse( cur );
8676                        if ( curValue !== finalValue ) {
8677                            elem.setAttribute( "class", finalValue );
8678                        }
8679                    }
8680                }
8681            }
8682
8683            return this;
8684        },
8685
8686        removeClass: function( value ) {
8687            var classes, elem, cur, curValue, clazz, j, finalValue,
8688                i = 0;
8689
8690            if ( isFunction( value ) ) {
8691                return this.each( function( j ) {
8692                    jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
8693                } );
8694            }
8695
8696            if ( !arguments.length ) {
8697                return this.attr( "class", "" );
8698            }
8699
8700            classes = classesToArray( value );
8701
8702            if ( classes.length ) {
8703                while ( ( elem = this[ i++ ] ) ) {
8704                    curValue = getClass( elem );
8705
8706                    // This expression is here for better compressibility (see addClass)
8707                    cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
8708
8709                    if ( cur ) {
8710                        j = 0;
8711                        while ( ( clazz = classes[ j++ ] ) ) {
8712
8713                            // Remove *all* instances
8714                            while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
8715                                cur = cur.replace( " " + clazz + " ", " " );
8716                            }
8717                        }
8718
8719                        // Only assign if different to avoid unneeded rendering.
8720                        finalValue = stripAndCollapse( cur );
8721                        if ( curValue !== finalValue ) {
8722                            elem.setAttribute( "class", finalValue );
8723                        }
8724                    }
8725                }
8726            }
8727
8728            return this;
8729        },
8730
8731        toggleClass: function( value, stateVal ) {
8732            var type = typeof value,
8733                isValidValue = type === "string" || Array.isArray( value );
8734
8735            if ( typeof stateVal === "boolean" && isValidValue ) {
8736                return stateVal ? this.addClass( value ) : this.removeClass( value );
8737            }
8738
8739            if ( isFunction( value ) ) {
8740                return this.each( function( i ) {
8741                    jQuery( this ).toggleClass(
8742                        value.call( this, i, getClass( this ), stateVal ),
8743                        stateVal
8744                    );
8745                } );
8746            }
8747
8748            return this.each( function() {
8749                var className, i, self, classNames;
8750
8751                if ( isValidValue ) {
8752
8753                    // Toggle individual class names
8754                    i = 0;
8755                    self = jQuery( this );
8756                    classNames = classesToArray( value );
8757
8758                    while ( ( className = classNames[ i++ ] ) ) {
8759
8760                        // Check each className given, space separated list
8761                        if ( self.hasClass( className ) ) {
8762                            self.removeClass( className );
8763                        } else {
8764                            self.addClass( className );
8765                        }
8766                    }
8767
8768                    // Toggle whole class name
8769                } else if ( value === undefined || type === "boolean" ) {
8770                    className = getClass( this );
8771                    if ( className ) {
8772
8773                        // Store className if set
8774                        dataPriv.set( this, "__className__", className );
8775                    }
8776
8777                    // If the element has a class name or if we're passed `false`,
8778                    // then remove the whole classname (if there was one, the above saved it).
8779                    // Otherwise bring back whatever was previously saved (if anything),
8780                    // falling back to the empty string if nothing was stored.
8781                    if ( this.setAttribute ) {
8782                        this.setAttribute( "class",
8783                            className || value === false ?
8784                                "" :
8785                                dataPriv.get( this, "__className__" ) || ""
8786                        );
8787                    }
8788                }
8789            } );
8790        },
8791
8792        hasClass: function( selector ) {
8793            var className, elem,
8794                i = 0;
8795
8796            className = " " + selector + " ";
8797            while ( ( elem = this[ i++ ] ) ) {
8798                if ( elem.nodeType === 1 &&
8799                    ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
8800                    return true;
8801                }
8802            }
8803
8804            return false;
8805        }
8806    } );
8807
8808
8809
8810
8811    var rreturn = /\r/g;
8812
8813    jQuery.fn.extend( {
8814        val: function( value ) {
8815            var hooks, ret, valueIsFunction,
8816                elem = this[ 0 ];
8817
8818            if ( !arguments.length ) {
8819                if ( elem ) {
8820                    hooks = jQuery.valHooks[ elem.type ] ||
8821                        jQuery.valHooks[ elem.nodeName.toLowerCase() ];
8822
8823                    if ( hooks &&
8824                        "get" in hooks &&
8825                        ( ret = hooks.get( elem, "value" ) ) !== undefined
8826                    ) {
8827                        return ret;
8828                    }
8829
8830                    ret = elem.value;
8831
8832                    // Handle most common string cases
8833                    if ( typeof ret === "string" ) {
8834                        return ret.replace( rreturn, "" );
8835                    }
8836
8837                    // Handle cases where value is null/undef or number
8838                    return ret == null ? "" : ret;
8839                }
8840
8841                return;
8842            }
8843
8844            valueIsFunction = isFunction( value );
8845
8846            return this.each( function( i ) {
8847                var val;
8848
8849                if ( this.nodeType !== 1 ) {
8850                    return;
8851                }
8852
8853                if ( valueIsFunction ) {
8854                    val = value.call( this, i, jQuery( this ).val() );
8855                } else {
8856                    val = value;
8857                }
8858
8859                // Treat null/undefined as ""; convert numbers to string
8860                if ( val == null ) {
8861                    val = "";
8862
8863                } else if ( typeof val === "number" ) {
8864                    val += "";
8865
8866                } else if ( Array.isArray( val ) ) {
8867                    val = jQuery.map( val, function( value ) {
8868                        return value == null ? "" : value + "";
8869                    } );
8870                }
8871
8872                hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
8873
8874                // If set returns undefined, fall back to normal setting
8875                if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
8876                    this.value = val;
8877                }
8878            } );
8879        }
8880    } );
8881
8882    jQuery.extend( {
8883        valHooks: {
8884            option: {
8885                get: function( elem ) {
8886
8887                    var val = jQuery.find.attr( elem, "value" );
8888                    return val != null ?
8889                        val :
8890
8891                        // Support: IE <=10 - 11 only
8892                        // option.text throws exceptions (#14686, #14858)
8893                        // Strip and collapse whitespace
8894                        // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
8895                        stripAndCollapse( jQuery.text( elem ) );
8896                }
8897            },
8898            select: {
8899                get: function( elem ) {
8900                    var value, option, i,
8901                        options = elem.options,
8902                        index = elem.selectedIndex,
8903                        one = elem.type === "select-one",
8904                        values = one ? null : [],
8905                        max = one ? index + 1 : options.length;
8906
8907                    if ( index < 0 ) {
8908                        i = max;
8909
8910                    } else {
8911                        i = one ? index : 0;
8912                    }
8913
8914                    // Loop through all the selected options
8915                    for ( ; i < max; i++ ) {
8916                        option = options[ i ];
8917
8918                        // Support: IE <=9 only
8919                        // IE8-9 doesn't update selected after form reset (#2551)
8920                        if ( ( option.selected || i === index ) &&
8921
8922                            // Don't return options that are disabled or in a disabled optgroup
8923                            !option.disabled &&
8924                            ( !option.parentNode.disabled ||
8925                                !nodeName( option.parentNode, "optgroup" ) ) ) {
8926
8927                            // Get the specific value for the option
8928                            value = jQuery( option ).val();
8929
8930                            // We don't need an array for one selects
8931                            if ( one ) {
8932                                return value;
8933                            }
8934
8935                            // Multi-Selects return an array
8936                            values.push( value );
8937                        }
8938                    }
8939
8940                    return values;
8941                },
8942
8943                set: function( elem, value ) {
8944                    var optionSet, option,
8945                        options = elem.options,
8946                        values = jQuery.makeArray( value ),
8947                        i = options.length;
8948
8949                    while ( i-- ) {
8950                        option = options[ i ];
8951
8952                        /* eslint-disable no-cond-assign */
8953
8954                        if ( option.selected =
8955                            jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
8956                        ) {
8957                            optionSet = true;
8958                        }
8959
8960                        /* eslint-enable no-cond-assign */
8961                    }
8962
8963                    // Force browsers to behave consistently when non-matching value is set
8964                    if ( !optionSet ) {
8965                        elem.selectedIndex = -1;
8966                    }
8967                    return values;
8968                }
8969            }
8970        }
8971    } );
8972
8973// Radios and checkboxes getter/setter
8974    jQuery.each( [ "radio", "checkbox" ], function() {
8975        jQuery.valHooks[ this ] = {
8976            set: function( elem, value ) {
8977                if ( Array.isArray( value ) ) {
8978                    return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
8979                }
8980            }
8981        };
8982        if ( !support.checkOn ) {
8983            jQuery.valHooks[ this ].get = function( elem ) {
8984                return elem.getAttribute( "value" ) === null ? "on" : elem.value;
8985            };
8986        }
8987    } );
8988
8989
8990
8991
8992// Return jQuery for attributes-only inclusion
8993
8994
8995    support.focusin = "onfocusin" in window;
8996
8997
8998    var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
8999        stopPropagationCallback = function( e ) {
9000            e.stopPropagation();
9001        };
9002
9003    jQuery.extend( jQuery.event, {
9004
9005        trigger: function( event, data, elem, onlyHandlers ) {
9006
9007            var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
9008                eventPath = [ elem || document ],
9009                type = hasOwn.call( event, "type" ) ? event.type : event,
9010                namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
9011
9012            cur = lastElement = tmp = elem = elem || document;
9013
9014            // Don't do events on text and comment nodes
9015            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
9016                return;
9017            }
9018
9019            // focus/blur morphs to focusin/out; ensure we're not firing them right now
9020            if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
9021                return;
9022            }
9023
9024            if ( type.indexOf( "." ) > -1 ) {
9025
9026                // Namespaced trigger; create a regexp to match event type in handle()
9027                namespaces = type.split( "." );
9028                type = namespaces.shift();
9029                namespaces.sort();
9030            }
vendor: 5,789 bytes, lines 9031-9184
9031            ontype = type.indexOf( ":" ) < 0 && "on" + type;
9032
9033            // Caller can pass in a jQuery.Event object, Object, or just an event type string
9034            event = event[ jQuery.expando ] ?
9035                event :
9036                new jQuery.Event( type, typeof event === "object" && event );
9037
9038            // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
9039            event.isTrigger = onlyHandlers ? 2 : 3;
9040            event.namespace = namespaces.join( "." );
9041            event.rnamespace = event.namespace ?
9042                new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
9043                null;
9044
9045            // Clean up the event in case it is being reused
9046            event.result = undefined;
9047            if ( !event.target ) {
9048                event.target = elem;
9049            }
9050
9051            // Clone any incoming data and prepend the event, creating the handler arg list
9052            data = data == null ?
9053                [ event ] :
9054                jQuery.makeArray( data, [ event ] );
9055
9056            // Allow special events to draw outside the lines
9057            special = jQuery.event.special[ type ] || {};
9058            if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
9059                return;
9060            }
9061
9062            // Determine event propagation path in advance, per W3C events spec (#9951)
9063            // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
9064            if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
9065
9066                bubbleType = special.delegateType || type;
9067                if ( !rfocusMorph.test( bubbleType + type ) ) {
9068                    cur = cur.parentNode;
9069                }
9070                for ( ; cur; cur = cur.parentNode ) {
9071                    eventPath.push( cur );
9072                    tmp = cur;
9073                }
9074
9075                // Only add window if we got to document (e.g., not plain obj or detached DOM)
9076                if ( tmp === ( elem.ownerDocument || document ) ) {
9077                    eventPath.push( tmp.defaultView || tmp.parentWindow || window );
9078                }
9079            }
9080
9081            // Fire handlers on the event path
9082            i = 0;
9083            while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
9084                lastElement = cur;
9085                event.type = i > 1 ?
9086                    bubbleType :
9087                    special.bindType || type;
9088
9089                // jQuery handler
9090                handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] &&
9091                    dataPriv.get( cur, "handle" );
9092                if ( handle ) {
9093                    handle.apply( cur, data );
9094                }
9095
9096                // Native handler
9097                handle = ontype && cur[ ontype ];
9098                if ( handle && handle.apply && acceptData( cur ) ) {
9099                    event.result = handle.apply( cur, data );
9100                    if ( event.result === false ) {
9101                        event.preventDefault();
9102                    }
9103                }
9104            }
9105            event.type = type;
9106
9107            // If nobody prevented the default action, do it now
9108            if ( !onlyHandlers && !event.isDefaultPrevented() ) {
9109
9110                if ( ( !special._default ||
9111                    special._default.apply( eventPath.pop(), data ) === false ) &&
9112                    acceptData( elem ) ) {
9113
9114                    // Call a native DOM method on the target with the same name as the event.
9115                    // Don't do default actions on window, that's where global variables be (#6170)
9116                    if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
9117
9118                        // Don't re-trigger an onFOO event when we call its FOO() method
9119                        tmp = elem[ ontype ];
9120
9121                        if ( tmp ) {
9122                            elem[ ontype ] = null;
9123                        }
9124
9125                        // Prevent re-triggering of the same event, since we already bubbled it above
9126                        jQuery.event.triggered = type;
9127
9128                        if ( event.isPropagationStopped() ) {
9129                            lastElement.addEventListener( type, stopPropagationCallback );
9130                        }
9131
9132                        elem[ type ]();
9133
9134                        if ( event.isPropagationStopped() ) {
9135                            lastElement.removeEventListener( type, stopPropagationCallback );
9136                        }
9137
9138                        jQuery.event.triggered = undefined;
9139
9140                        if ( tmp ) {
9141                            elem[ ontype ] = tmp;
9142                        }
9143                    }
9144                }
9145            }
9146
9147            return event.result;
9148        },
9149
9150        // Piggyback on a donor event to simulate a different one
9151        // Used only for `focus(in | out)` events
9152        simulate: function( type, elem, event ) {
9153            var e = jQuery.extend(
9154                new jQuery.Event(),
9155                event,
9156                {
9157                    type: type,
9158                    isSimulated: true
9159                }
9160            );
9161
9162            jQuery.event.trigger( e, null, elem );
9163        }
9164
9165    } );
9166
9167    jQuery.fn.extend( {
9168
9169        trigger: function( type, data ) {
9170            return this.each( function() {
9171                jQuery.event.trigger( type, data, this );
9172            } );
9173        },
9174        triggerHandler: function( type, data ) {
9175            var elem = this[ 0 ];
9176            if ( elem ) {
9177                return jQuery.event.trigger( type, data, elem, true );
9178            }
9179        }
9180    } );
9181
9182
9183// Support: Firefox <=44
9184// Firefox doesn't have focus(in | out) events
vendor: 5,999 bytes, lines 9185-9360
9185// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
9186//
9187// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
9188// focus(in | out) events fire after focus & blur events,
9189// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
9190// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
9191    if ( !support.focusin ) {
9192        jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
9193
9194            // Attach a single capturing handler on the document while someone wants focusin/focusout
9195            var handler = function( event ) {
9196                jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
9197            };
9198
9199            jQuery.event.special[ fix ] = {
9200                setup: function() {
9201
9202                    // Handle: regular nodes (via `this.ownerDocument`), window
9203                    // (via `this.document`) & document (via `this`).
9204                    var doc = this.ownerDocument || this.document || this,
9205                        attaches = dataPriv.access( doc, fix );
9206
9207                    if ( !attaches ) {
9208                        doc.addEventListener( orig, handler, true );
9209                    }
9210                    dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
9211                },
9212                teardown: function() {
9213                    var doc = this.ownerDocument || this.document || this,
9214                        attaches = dataPriv.access( doc, fix ) - 1;
9215
9216                    if ( !attaches ) {
9217                        doc.removeEventListener( orig, handler, true );
9218                        dataPriv.remove( doc, fix );
9219
9220                    } else {
9221                        dataPriv.access( doc, fix, attaches );
9222                    }
9223                }
9224            };
9225        } );
9226    }
9227    var location = window.location;
9228
9229    var nonce = { guid: Date.now() };
9230
9231    var rquery = ( /\?/ );
9232
9233
9234
9235// Cross-browser xml parsing
9236    jQuery.parseXML = function( data ) {
9237        var xml, parserErrorElem;
9238        if ( !data || typeof data !== "string" ) {
9239            return null;
9240        }
9241
9242        // Support: IE 9 - 11 only
9243        // IE throws on parseFromString with invalid input.
9244        try {
9245            xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
9246        } catch ( e ) {}
9247
9248        parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ];
9249        if ( !xml || parserErrorElem ) {
9250            jQuery.error( "Invalid XML: " + (
9251                parserErrorElem ?
9252                    jQuery.map( parserErrorElem.childNodes, function( el ) {
9253                        return el.textContent;
9254                    } ).join( "\n" ) :
9255                    data
9256            ) );
9257        }
9258        return xml;
9259    };
9260
9261
9262    var
9263        rbracket = /\[\]$/,
9264        rCRLF = /\r?\n/g,
9265        rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
9266        rsubmittable = /^(?:input|select|textarea|keygen)/i;
9267
9268    function buildParams( prefix, obj, traditional, add ) {
9269        var name;
9270
9271        if ( Array.isArray( obj ) ) {
9272
9273            // Serialize array item.
9274            jQuery.each( obj, function( i, v ) {
9275                if ( traditional || rbracket.test( prefix ) ) {
9276
9277                    // Treat each array item as a scalar.
9278                    add( prefix, v );
9279
9280                } else {
9281
9282                    // Item is non-scalar (array or object), encode its numeric index.
9283                    buildParams(
9284                        prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
9285                        v,
9286                        traditional,
9287                        add
9288                    );
9289                }
9290            } );
9291
9292        } else if ( !traditional && toType( obj ) === "object" ) {
9293
9294            // Serialize object item.
9295            for ( name in obj ) {
9296                buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
9297            }
9298
9299        } else {
9300
9301            // Serialize scalar item.
9302            add( prefix, obj );
9303        }
9304    }
9305
9306// Serialize an array of form elements or a set of
9307// key/values into a query string
9308    jQuery.param = function( a, traditional ) {
9309        var prefix,
9310            s = [],
9311            add = function( key, valueOrFunction ) {
9312
9313                // If value is a function, invoke it and use its return value
9314                var value = isFunction( valueOrFunction ) ?
9315                    valueOrFunction() :
9316                    valueOrFunction;
9317
9318                s[ s.length ] = encodeURIComponent( key ) + "=" +
9319                    encodeURIComponent( value == null ? "" : value );
9320            };
9321
9322        if ( a == null ) {
9323            return "";
9324        }
9325
9326        // If an array was passed in, assume that it is an array of form elements.
9327        if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
9328
9329            // Serialize the form elements
9330            jQuery.each( a, function() {
9331                add( this.name, this.value );
9332            } );
9333
9334        } else {
9335
9336            // If traditional, encode the "old" way (the way 1.3.2 or older
9337            // did it), otherwise encode params recursively.
9338            for ( prefix in a ) {
9339                buildParams( prefix, a[ prefix ], traditional, add );
9340            }
9341        }
9342
9343        // Return the resulting serialization
9344        return s.join( "&" );
9345    };
9346
9347    jQuery.fn.extend( {
9348        serialize: function() {
9349            return jQuery.param( this.serializeArray() );
9350        },
9351        serializeArray: function() {
9352            return this.map( function() {
9353
9354                // Can add propHook for "elements" to filter or add form elements
9355                var elements = jQuery.prop( this, "elements" );
9356                return elements ? jQuery.makeArray( elements ) : this;
9357            } ).filter( function() {
9358                var type = this.type;
9359
9360                // Use .is( ":disabled" ) so that fieldset[disabled] w
vendor: 4,128 bytes, lines 9360-9470
9360orks
9361                return this.name && !jQuery( this ).is( ":disabled" ) &&
9362                    rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
9363                    ( this.checked || !rcheckableType.test( type ) );
9364            } ).map( function( _i, elem ) {
9365                var val = jQuery( this ).val();
9366
9367                if ( val == null ) {
9368                    return null;
9369                }
9370
9371                if ( Array.isArray( val ) ) {
9372                    return jQuery.map( val, function( val ) {
9373                        return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
9374                    } );
9375                }
9376
9377                return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
9378            } ).get();
9379        }
9380    } );
9381
9382
9383    var
9384        r20 = /%20/g,
9385        rhash = /#.*$/,
9386        rantiCache = /([?&])_=[^&]*/,
9387        rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
9388
9389        // #7653, #8125, #8152: local protocol detection
9390        rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
9391        rnoContent = /^(?:GET|HEAD)$/,
9392        rprotocol = /^\/\//,
9393
9394        /* Prefilters
9395	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
9396	 * 2) These are called:
9397	 *    - BEFORE asking for a transport
9398	 *    - AFTER param serialization (s.data is a string if s.processData is true)
9399	 * 3) key is the dataType
9400	 * 4) the catchall symbol "*" can be used
9401	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
9402	 */
9403        prefilters = {},
9404
9405        /* Transports bindings
9406	 * 1) key is the dataType
9407	 * 2) the catchall symbol "*" can be used
9408	 * 3) selection will start with transport dataType and THEN go to "*" if needed
9409	 */
9410        transports = {},
9411
9412        // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
9413        allTypes = "*/".concat( "*" ),
9414
9415        // Anchor tag for parsing the document origin
9416        originAnchor = document.createElement( "a" );
9417
9418    originAnchor.href = location.href;
9419
9420// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
9421    function addToPrefiltersOrTransports( structure ) {
9422
9423        // dataTypeExpression is optional and defaults to "*"
9424        return function( dataTypeExpression, func ) {
9425
9426            if ( typeof dataTypeExpression !== "string" ) {
9427                func = dataTypeExpression;
9428                dataTypeExpression = "*";
9429            }
9430
9431            var dataType,
9432                i = 0,
9433                dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
9434
9435            if ( isFunction( func ) ) {
9436
9437                // For each dataType in the dataTypeExpression
9438                while ( ( dataType = dataTypes[ i++ ] ) ) {
9439
9440                    // Prepend if requested
9441                    if ( dataType[ 0 ] === "+" ) {
9442                        dataType = dataType.slice( 1 ) || "*";
9443                        ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
9444
9445                        // Otherwise append
9446                    } else {
9447                        ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
9448                    }
9449                }
9450            }
9451        };
9452    }
9453
9454// Base inspection function for prefilters and transports
9455    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
9456
9457        var inspected = {},
9458            seekingTransport = ( structure === transports );
9459
9460        function inspect( dataType ) {
9461            var selected;
9462            inspected[ dataType ] = true;
9463            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
9464                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
9465                if ( typeof dataTypeOrTransport === "string" &&
9466                    !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
9467
9468                    options.dataTypes.unshift( dataTypeOrTransport );
9469                    inspect( dataTypeOrTransport );
9470                    return false;
vendor: 15,305 bytes, lines 9471-9908
9471                } else if ( seekingTransport ) {
9472                    return !( selected = dataTypeOrTransport );
9473                }
9474            } );
9475            return selected;
9476        }
9477
9478        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
9479    }
9480
9481// A special extend for ajax options
9482// that takes "flat" options (not to be deep extended)
9483// Fixes #9887
9484    function ajaxExtend( target, src ) {
9485        var key, deep,
9486            flatOptions = jQuery.ajaxSettings.flatOptions || {};
9487
9488        for ( key in src ) {
9489            if ( src[ key ] !== undefined ) {
9490                ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
9491            }
9492        }
9493        if ( deep ) {
9494            jQuery.extend( true, target, deep );
9495        }
9496
9497        return target;
9498    }
9499
9500    /* Handles responses to an ajax request:
9501 * - finds the right dataType (mediates between content-type and expected dataType)
9502 * - returns the corresponding response
9503 */
9504    function ajaxHandleResponses( s, jqXHR, responses ) {
9505
9506        var ct, type, finalDataType, firstDataType,
9507            contents = s.contents,
9508            dataTypes = s.dataTypes;
9509
9510        // Remove auto dataType and get content-type in the process
9511        while ( dataTypes[ 0 ] === "*" ) {
9512            dataTypes.shift();
9513            if ( ct === undefined ) {
9514                ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
9515            }
9516        }
9517
9518        // Check if we're dealing with a known content-type
9519        if ( ct ) {
9520            for ( type in contents ) {
9521                if ( contents[ type ] && contents[ type ].test( ct ) ) {
9522                    dataTypes.unshift( type );
9523                    break;
9524                }
9525            }
9526        }
9527
9528        // Check to see if we have a response for the expected dataType
9529        if ( dataTypes[ 0 ] in responses ) {
9530            finalDataType = dataTypes[ 0 ];
9531        } else {
9532
9533            // Try convertible dataTypes
9534            for ( type in responses ) {
9535                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
9536                    finalDataType = type;
9537                    break;
9538                }
9539                if ( !firstDataType ) {
9540                    firstDataType = type;
9541                }
9542            }
9543
9544            // Or just use first one
9545            finalDataType = finalDataType || firstDataType;
9546        }
9547
9548        // If we found a dataType
9549        // We add the dataType to the list if needed
9550        // and return the corresponding response
9551        if ( finalDataType ) {
9552            if ( finalDataType !== dataTypes[ 0 ] ) {
9553                dataTypes.unshift( finalDataType );
9554            }
9555            return responses[ finalDataType ];
9556        }
9557    }
9558
9559    /* Chain conversions given the request and the original response
9560 * Also sets the responseXXX fields on the jqXHR instance
9561 */
9562    function ajaxConvert( s, response, jqXHR, isSuccess ) {
9563        var conv2, current, conv, tmp, prev,
9564            converters = {},
9565
9566            // Work with a copy of dataTypes in case we need to modify it for conversion
9567            dataTypes = s.dataTypes.slice();
9568
9569        // Create converters map with lowercased keys
9570        if ( dataTypes[ 1 ] ) {
9571            for ( conv in s.converters ) {
9572                converters[ conv.toLowerCase() ] = s.converters[ conv ];
9573            }
9574        }
9575
9576        current = dataTypes.shift();
9577
9578        // Convert to each sequential dataType
9579        while ( current ) {
9580
9581            if ( s.responseFields[ current ] ) {
9582                jqXHR[ s.responseFields[ current ] ] = response;
9583            }
9584
9585            // Apply the dataFilter if provided
9586            if ( !prev && isSuccess && s.dataFilter ) {
9587                response = s.dataFilter( response, s.dataType );
9588            }
9589
9590            prev = current;
9591            current = dataTypes.shift();
9592
9593            if ( current ) {
9594
9595                // There's only work to do if current dataType is non-auto
9596                if ( current === "*" ) {
9597
9598                    current = prev;
9599
9600                    // Convert response if prev dataType is non-auto and differs from current
9601                } else if ( prev !== "*" && prev !== current ) {
9602
9603                    // Seek a direct converter
9604                    conv = converters[ prev + " " + current ] || converters[ "* " + current ];
9605
9606                    // If none found, seek a pair
9607                    if ( !conv ) {
9608                        for ( conv2 in converters ) {
9609
9610                            // If conv2 outputs current
9611                            tmp = conv2.split( " " );
9612                            if ( tmp[ 1 ] === current ) {
9613
9614                                // If prev can be converted to accepted input
9615                                conv = converters[ prev + " " + tmp[ 0 ] ] ||
9616                                    converters[ "* " + tmp[ 0 ] ];
9617                                if ( conv ) {
9618
9619                                    // Condense equivalence converters
9620                                    if ( conv === true ) {
9621                                        conv = converters[ conv2 ];
9622
9623                                        // Otherwise, insert the intermediate dataType
9624                                    } else if ( converters[ conv2 ] !== true ) {
9625                                        current = tmp[ 0 ];
9626                                        dataTypes.unshift( tmp[ 1 ] );
9627                                    }
9628                                    break;
9629                                }
9630                            }
9631                        }
9632                    }
9633
9634                    // Apply converter (if not an equivalence)
9635                    if ( conv !== true ) {
9636
9637                        // Unless errors are allowed to bubble, catch and return them
9638                        if ( conv && s.throws ) {
9639                            response = conv( response );
9640                        } else {
9641                            try {
9642                                response = conv( response );
9643                            } catch ( e ) {
9644                                return {
9645                                    state: "parsererror",
9646                                    error: conv ? e : "No conversion from " + prev + " to " + current
9647                                };
9648                            }
9649                        }
9650                    }
9651                }
9652            }
9653        }
9654
9655        return { state: "success", data: response };
9656    }
9657
9658    jQuery.extend( {
9659
9660        // Counter for holding the number of active queries
9661        active: 0,
9662
9663        // Last-Modified header cache for next request
9664        lastModified: {},
9665        etag: {},
9666
9667        ajaxSettings: {
9668            url: location.href,
9669            type: "GET",
9670            isLocal: rlocalProtocol.test( location.protocol ),
9671            global: true,
9672            processData: true,
9673            async: true,
9674            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
9675
9676            /*
9677		timeout: 0,
9678		data: null,
9679		dataType: null,
9680		username: null,
9681		password: null,
9682		cache: null,
9683		throws: false,
9684		traditional: false,
9685		headers: {},
9686		*/
9687
9688            accepts: {
9689                "*": allTypes,
9690                text: "text/plain",
9691                html: "text/html",
9692                xml: "application/xml, text/xml",
9693                json: "application/json, text/javascript"
9694            },
9695
9696            contents: {
9697                xml: /\bxml\b/,
9698                html: /\bhtml/,
9699                json: /\bjson\b/
9700            },
9701
9702            responseFields: {
9703                xml: "responseXML",
9704                text: "responseText",
9705                json: "responseJSON"
9706            },
9707
9708            // Data converters
9709            // Keys separate source (or catchall "*") and destination types with a single space
9710            converters: {
9711
9712                // Convert anything to text
9713                "* text": String,
9714
9715                // Text to html (true = no transformation)
9716                "text html": true,
9717
9718                // Evaluate text as a json expression
9719                "text json": JSON.parse,
9720
9721                // Parse text as xml
9722                "text xml": jQuery.parseXML
9723            },
9724
9725            // For options that shouldn't be deep extended:
9726            // you can add your own custom options here if
9727            // and when you create one that shouldn't be
9728            // deep extended (see ajaxExtend)
9729            flatOptions: {
9730                url: true,
9731                context: true
9732            }
9733        },
9734
9735        // Creates a full fledged settings object into target
9736        // with both ajaxSettings and settings fields.
9737        // If target is omitted, writes into ajaxSettings.
9738        ajaxSetup: function( target, settings ) {
9739            return settings ?
9740
9741                // Building a settings object
9742                ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
9743
9744                // Extending ajaxSettings
9745                ajaxExtend( jQuery.ajaxSettings, target );
9746        },
9747
9748        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
9749        ajaxTransport: addToPrefiltersOrTransports( transports ),
9750
9751        // Main method
9752        ajax: function( url, options ) {
9753
9754            // If url is an object, simulate pre-1.5 signature
9755            if ( typeof url === "object" ) {
9756                options = url;
9757                url = undefined;
9758            }
9759
9760            // Force options to be an object
9761            options = options || {};
9762
9763            var transport,
9764
9765                // URL without anti-cache param
9766                cacheURL,
9767
9768                // Response headers
9769                responseHeadersString,
9770                responseHeaders,
9771
9772                // timeout handle
9773                timeoutTimer,
9774
9775                // Url cleanup var
9776                urlAnchor,
9777
9778                // Request state (becomes false upon send and true upon completion)
9779                completed,
9780
9781                // To know if global events are to be dispatched
9782                fireGlobals,
9783
9784                // Loop variable
9785                i,
9786
9787                // uncached part of the url
9788                uncached,
9789
9790                // Create the final options object
9791                s = jQuery.ajaxSetup( {}, options ),
9792
9793                // Callbacks context
9794                callbackContext = s.context || s,
9795
9796                // Context for global events is callbackContext if it is a DOM node or jQuery collection
9797                globalEventContext = s.context &&
9798                ( callbackContext.nodeType || callbackContext.jquery ) ?
9799                    jQuery( callbackContext ) :
9800                    jQuery.event,
9801
9802                // Deferreds
9803                deferred = jQuery.Deferred(),
9804                completeDeferred = jQuery.Callbacks( "once memory" ),
9805
9806                // Status-dependent callbacks
9807                statusCode = s.statusCode || {},
9808
9809                // Headers (they are sent all at once)
9810                requestHeaders = {},
9811                requestHeadersNames = {},
9812
9813                // Default abort message
9814                strAbort = "canceled",
9815
9816                // Fake xhr
9817                jqXHR = {
9818                    readyState: 0,
9819
9820                    // Builds headers hashtable if needed
9821                    getResponseHeader: function( key ) {
9822                        var match;
9823                        if ( completed ) {
9824                            if ( !responseHeaders ) {
9825                                responseHeaders = {};
9826                                while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
9827                                    responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
9828                                        ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
9829                                            .concat( match[ 2 ] );
9830                                }
9831                            }
9832                            match = responseHeaders[ key.toLowerCase() + " " ];
9833                        }
9834                        return match == null ? null : match.join( ", " );
9835                    },
9836
9837                    // Raw string
9838                    getAllResponseHeaders: function() {
9839                        return completed ? responseHeadersString : null;
9840                    },
9841
9842                    // Caches the header
9843                    setRequestHeader: function( name, value ) {
9844                        if ( completed == null ) {
9845                            name = requestHeadersNames[ name.toLowerCase() ] =
9846                                requestHeadersNames[ name.toLowerCase() ] || name;
9847                            requestHeaders[ name ] = value;
9848                        }
9849                        return this;
9850                    },
9851
9852                    // Overrides response content-type header
9853                    overrideMimeType: function( type ) {
9854                        if ( completed == null ) {
9855                            s.mimeType = type;
9856                        }
9857                        return this;
9858                    },
9859
9860                    // Status-dependent callbacks
9861                    statusCode: function( map ) {
9862                        var code;
9863                        if ( map ) {
9864                            if ( completed ) {
9865
9866                                // Execute the appropriate callbacks
9867                                jqXHR.always( map[ jqXHR.status ] );
9868                            } else {
9869
9870                                // Lazy-add the new callbacks in a way that preserves old ones
9871                                for ( code in map ) {
9872                                    statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
9873                                }
9874                            }
9875                        }
9876                        return this;
9877                    },
9878
9879                    // Cancel the request
9880                    abort: function( statusText ) {
9881                        var finalText = statusText || strAbort;
9882                        if ( transport ) {
9883                            transport.abort( finalText );
9884                        }
9885                        done( 0, finalText );
9886                        return this;
9887                    }
9888                };
9889
9890            // Attach deferreds
9891            deferred.promise( jqXHR );
9892
9893            // Add protocol if not provided (prefilters might expect it)
9894            // Handle falsy url in the settings object (#10093: consistency with old signature)
9895            // We also use the url parameter if available
9896            s.url = ( ( url || s.url || location.href ) + "" )
9897                .replace( rprotocol, location.protocol + "//" );
9898
9899            // Alias method option to type as per ticket #12004
9900            s.type = options.method || options.type || s.method || s.type;
9901
9902            // Extract dataTypes list
9903            s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
9904
9905            // A cross-domain request is in order when the origin doesn't match the current origin.
9906            if ( s.crossDomain == null ) {
9907                urlAnchor = document.createElement( "a" );
9908
vendor: 41,672 bytes, lines 9909-11038
9909                // Support: IE <=8 - 11, Edge 12 - 15
9910                // IE throws exception on accessing the href property if url is malformed,
9911                // e.g. http://example.com:80x/
9912                try {
9913                    urlAnchor.href = s.url;
9914
9915                    // Support: IE <=8 - 11 only
9916                    // Anchor's host property isn't correctly set when s.url is relative
9917                    urlAnchor.href = urlAnchor.href;
9918                    s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
9919                        urlAnchor.protocol + "//" + urlAnchor.host;
9920                } catch ( e ) {
9921
9922                    // If there is an error parsing the URL, assume it is crossDomain,
9923                    // it can be rejected by the transport if it is invalid
9924                    s.crossDomain = true;
9925                }
9926            }
9927
9928            // Convert data if not already a string
9929            if ( s.data && s.processData && typeof s.data !== "string" ) {
9930                s.data = jQuery.param( s.data, s.traditional );
9931            }
9932
9933            // Apply prefilters
9934            inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
9935
9936            // If request was aborted inside a prefilter, stop there
9937            if ( completed ) {
9938                return jqXHR;
9939            }
9940
9941            // We can fire global events as of now if asked to
9942            // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
9943            fireGlobals = jQuery.event && s.global;
9944
9945            // Watch for a new set of requests
9946            if ( fireGlobals && jQuery.active++ === 0 ) {
9947                jQuery.event.trigger( "ajaxStart" );
9948            }
9949
9950            // Uppercase the type
9951            s.type = s.type.toUpperCase();
9952
9953            // Determine if request has content
9954            s.hasContent = !rnoContent.test( s.type );
9955
9956            // Save the URL in case we're toying with the If-Modified-Since
9957            // and/or If-None-Match header later on
9958            // Remove hash to simplify url manipulation
9959            cacheURL = s.url.replace( rhash, "" );
9960
9961            // More options handling for requests with no content
9962            if ( !s.hasContent ) {
9963
9964                // Remember the hash so we can put it back
9965                uncached = s.url.slice( cacheURL.length );
9966
9967                // If data is available and should be processed, append data to url
9968                if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
9969                    cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
9970
9971                    // #9682: remove data so that it's not used in an eventual retry
9972                    delete s.data;
9973                }
9974
9975                // Add or update anti-cache param if needed
9976                if ( s.cache === false ) {
9977                    cacheURL = cacheURL.replace( rantiCache, "$1" );
9978                    uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) +
9979                        uncached;
9980                }
9981
9982                // Put hash and anti-cache on the URL that will be requested (gh-1732)
9983                s.url = cacheURL + uncached;
9984
9985                // Change '%20' to '+' if this is encoded form body content (gh-2658)
9986            } else if ( s.data && s.processData &&
9987                ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
9988                s.data = s.data.replace( r20, "+" );
9989            }
9990
9991            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
9992            if ( s.ifModified ) {
9993                if ( jQuery.lastModified[ cacheURL ] ) {
9994                    jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
9995                }
9996                if ( jQuery.etag[ cacheURL ] ) {
9997                    jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
9998                }
9999            }
10000
10001            // Set the correct header, if data is being sent
10002            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
10003                jqXHR.setRequestHeader( "Content-Type", s.contentType );
10004            }
10005
10006            // Set the Accepts header for the server, depending on the dataType
10007            jqXHR.setRequestHeader(
10008                "Accept",
10009                s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
10010                    s.accepts[ s.dataTypes[ 0 ] ] +
10011                    ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
10012                    s.accepts[ "*" ]
10013            );
10014
10015            // Check for headers option
10016            for ( i in s.headers ) {
10017                jqXHR.setRequestHeader( i, s.headers[ i ] );
10018            }
10019
10020            // Allow custom headers/mimetypes and early abort
10021            if ( s.beforeSend &&
10022                ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
10023
10024                // Abort if not done already and return
10025                return jqXHR.abort();
10026            }
10027
10028            // Aborting is no longer a cancellation
10029            strAbort = "abort";
10030
10031            // Install callbacks on deferreds
10032            completeDeferred.add( s.complete );
10033            jqXHR.done( s.success );
10034            jqXHR.fail( s.error );
10035
10036            // Get transport
10037            transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
10038
10039            // If no transport, we auto-abort
10040            if ( !transport ) {
10041                done( -1, "No Transport" );
10042            } else {
10043                jqXHR.readyState = 1;
10044
10045                // Send global event
10046                if ( fireGlobals ) {
10047                    globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
10048                }
10049
10050                // If request was aborted inside ajaxSend, stop there
10051                if ( completed ) {
10052                    return jqXHR;
10053                }
10054
10055                // Timeout
10056                if ( s.async && s.timeout > 0 ) {
10057                    timeoutTimer = window.setTimeout( function() {
10058                        jqXHR.abort( "timeout" );
10059                    }, s.timeout );
10060                }
10061
10062                try {
10063                    completed = false;
10064                    transport.send( requestHeaders, done );
10065                } catch ( e ) {
10066
10067                    // Rethrow post-completion exceptions
10068                    if ( completed ) {
10069                        throw e;
10070                    }
10071
10072                    // Propagate others as results
10073                    done( -1, e );
10074                }
10075            }
10076
10077            // Callback for when everything is done
10078            function done( status, nativeStatusText, responses, headers ) {
10079                var isSuccess, success, error, response, modified,
10080                    statusText = nativeStatusText;
10081
10082                // Ignore repeat invocations
10083                if ( completed ) {
10084                    return;
10085                }
10086
10087                completed = true;
10088
10089                // Clear timeout if it exists
10090                if ( timeoutTimer ) {
10091                    window.clearTimeout( timeoutTimer );
10092                }
10093
10094                // Dereference transport for early garbage collection
10095                // (no matter how long the jqXHR object will be used)
10096                transport = undefined;
10097
10098                // Cache response headers
10099                responseHeadersString = headers || "";
10100
10101                // Set readyState
10102                jqXHR.readyState = status > 0 ? 4 : 0;
10103
10104                // Determine if successful
10105                isSuccess = status >= 200 && status < 300 || status === 304;
10106
10107                // Get response data
10108                if ( responses ) {
10109                    response = ajaxHandleResponses( s, jqXHR, responses );
10110                }
10111
10112                // Use a noop converter for missing script but not if jsonp
10113                if ( !isSuccess &&
10114                    jQuery.inArray( "script", s.dataTypes ) > -1 &&
10115                    jQuery.inArray( "json", s.dataTypes ) < 0 ) {
10116                    s.converters[ "text script" ] = function() {};
10117                }
10118
10119                // Convert no matter what (that way responseXXX fields are always set)
10120                response = ajaxConvert( s, response, jqXHR, isSuccess );
10121
10122                // If successful, handle type chaining
10123                if ( isSuccess ) {
10124
10125                    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
10126                    if ( s.ifModified ) {
10127                        modified = jqXHR.getResponseHeader( "Last-Modified" );
10128                        if ( modified ) {
10129                            jQuery.lastModified[ cacheURL ] = modified;
10130                        }
10131                        modified = jqXHR.getResponseHeader( "etag" );
10132                        if ( modified ) {
10133                            jQuery.etag[ cacheURL ] = modified;
10134                        }
10135                    }
10136
10137                    // if no content
10138                    if ( status === 204 || s.type === "HEAD" ) {
10139                        statusText = "nocontent";
10140
10141                        // if not modified
10142                    } else if ( status === 304 ) {
10143                        statusText = "notmodified";
10144
10145                        // If we have data, let's convert it
10146                    } else {
10147                        statusText = response.state;
10148                        success = response.data;
10149                        error = response.error;
10150                        isSuccess = !error;
10151                    }
10152                } else {
10153
10154                    // Extract error from statusText and normalize for non-aborts
10155                    error = statusText;
10156                    if ( status || !statusText ) {
10157                        statusText = "error";
10158                        if ( status < 0 ) {
10159                            status = 0;
10160                        }
10161                    }
10162                }
10163
10164                // Set data for the fake xhr object
10165                jqXHR.status = status;
10166                jqXHR.statusText = ( nativeStatusText || statusText ) + "";
10167
10168                // Success/Error
10169                if ( isSuccess ) {
10170                    deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
10171                } else {
10172                    deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
10173                }
10174
10175                // Status-dependent callbacks
10176                jqXHR.statusCode( statusCode );
10177                statusCode = undefined;
10178
10179                if ( fireGlobals ) {
10180                    globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
10181                        [ jqXHR, s, isSuccess ? success : error ] );
10182                }
10183
10184                // Complete
10185                completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
10186
10187                if ( fireGlobals ) {
10188                    globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
10189
10190                    // Handle the global AJAX counter
10191                    if ( !( --jQuery.active ) ) {
10192                        jQuery.event.trigger( "ajaxStop" );
10193                    }
10194                }
10195            }
10196
10197            return jqXHR;
10198        },
10199
10200        getJSON: function( url, data, callback ) {
10201            return jQuery.get( url, data, callback, "json" );
10202        },
10203
10204        getScript: function( url, callback ) {
10205            return jQuery.get( url, undefined, callback, "script" );
10206        }
10207    } );
10208
10209    jQuery.each( [ "get", "post" ], function( _i, method ) {
10210        jQuery[ method ] = function( url, data, callback, type ) {
10211
10212            // Shift arguments if data argument was omitted
10213            if ( isFunction( data ) ) {
10214                type = type || callback;
10215                callback = data;
10216                data = undefined;
10217            }
10218
10219            // The url can be an options object (which then must have .url)
10220            return jQuery.ajax( jQuery.extend( {
10221                url: url,
10222                type: method,
10223                dataType: type,
10224                data: data,
10225                success: callback
10226            }, jQuery.isPlainObject( url ) && url ) );
10227        };
10228    } );
10229
10230    jQuery.ajaxPrefilter( function( s ) {
10231        var i;
10232        for ( i in s.headers ) {
10233            if ( i.toLowerCase() === "content-type" ) {
10234                s.contentType = s.headers[ i ] || "";
10235            }
10236        }
10237    } );
10238
10239
10240    jQuery._evalUrl = function( url, options, doc ) {
10241        return jQuery.ajax( {
10242            url: url,
10243
10244            // Make this explicit, since user can override this through ajaxSetup (#11264)
10245            type: "GET",
10246            dataType: "script",
10247            cache: true,
10248            async: false,
10249            global: false,
10250
10251            // Only evaluate the response if it is successful (gh-4126)
10252            // dataFilter is not invoked for failure responses, so using it instead
10253            // of the default converter is kludgy but it works.
10254            converters: {
10255                "text script": function() {}
10256            },
10257            dataFilter: function( response ) {
10258                jQuery.globalEval( response, options, doc );
10259            }
10260        } );
10261    };
10262
10263
10264    jQuery.fn.extend( {
10265        wrapAll: function( html ) {
10266            var wrap;
10267
10268            if ( this[ 0 ] ) {
10269                if ( isFunction( html ) ) {
10270                    html = html.call( this[ 0 ] );
10271                }
10272
10273                // The elements to wrap the target around
10274                wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
10275
10276                if ( this[ 0 ].parentNode ) {
10277                    wrap.insertBefore( this[ 0 ] );
10278                }
10279
10280                wrap.map( function() {
10281                    var elem = this;
10282
10283                    while ( elem.firstElementChild ) {
10284                        elem = elem.firstElementChild;
10285                    }
10286
10287                    return elem;
10288                } ).append( this );
10289            }
10290
10291            return this;
10292        },
10293
10294        wrapInner: function( html ) {
10295            if ( isFunction( html ) ) {
10296                return this.each( function( i ) {
10297                    jQuery( this ).wrapInner( html.call( this, i ) );
10298                } );
10299            }
10300
10301            return this.each( function() {
10302                var self = jQuery( this ),
10303                    contents = self.contents();
10304
10305                if ( contents.length ) {
10306                    contents.wrapAll( html );
10307
10308                } else {
10309                    self.append( html );
10310                }
10311            } );
10312        },
10313
10314        wrap: function( html ) {
10315            var htmlIsFunction = isFunction( html );
10316
10317            return this.each( function( i ) {
10318                jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
10319            } );
10320        },
10321
10322        unwrap: function( selector ) {
10323            this.parent( selector ).not( "body" ).each( function() {
10324                jQuery( this ).replaceWith( this.childNodes );
10325            } );
10326            return this;
10327        }
10328    } );
10329
10330
10331    jQuery.expr.pseudos.hidden = function( elem ) {
10332        return !jQuery.expr.pseudos.visible( elem );
10333    };
10334    jQuery.expr.pseudos.visible = function( elem ) {
10335        return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
10336    };
10337
10338
10339
10340
10341    jQuery.ajaxSettings.xhr = function() {
10342        try {
10343            return new window.XMLHttpRequest();
10344        } catch ( e ) {}
10345    };
10346
10347    var xhrSuccessStatus = {
10348
10349            // File protocol always yields status code 0, assume 200
10350            0: 200,
10351
10352            // Support: IE <=9 only
10353            // #1450: sometimes IE returns 1223 when it should be 204
10354            1223: 204
10355        },
10356        xhrSupported = jQuery.ajaxSettings.xhr();
10357
10358    support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
10359    support.ajax = xhrSupported = !!xhrSupported;
10360
10361    jQuery.ajaxTransport( function( options ) {
10362        var callback, errorCallback;
10363
10364        // Cross domain only allowed if supported through XMLHttpRequest
10365        if ( support.cors || xhrSupported && !options.crossDomain ) {
10366            return {
10367                send: function( headers, complete ) {
10368                    var i,
10369                        xhr = options.xhr();
10370
10371                    xhr.open(
10372                        options.type,
10373                        options.url,
10374                        options.async,
10375                        options.username,
10376                        options.password
10377                    );
10378
10379                    // Apply custom fields if provided
10380                    if ( options.xhrFields ) {
10381                        for ( i in options.xhrFields ) {
10382                            xhr[ i ] = options.xhrFields[ i ];
10383                        }
10384                    }
10385
10386                    // Override mime type if needed
10387                    if ( options.mimeType && xhr.overrideMimeType ) {
10388                        xhr.overrideMimeType( options.mimeType );
10389                    }
10390
10391                    // X-Requested-With header
10392                    // For cross-domain requests, seeing as conditions for a preflight are
10393                    // akin to a jigsaw puzzle, we simply never set it to be sure.
10394                    // (it can always be set on a per-request basis or even using ajaxSetup)
10395                    // For same-domain requests, won't change header if already provided.
10396                    if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
10397                        headers[ "X-Requested-With" ] = "XMLHttpRequest";
10398                    }
10399
10400                    // Set headers
10401                    for ( i in headers ) {
10402                        xhr.setRequestHeader( i, headers[ i ] );
10403                    }
10404
10405                    // Callback
10406                    callback = function( type ) {
10407                        return function() {
10408                            if ( callback ) {
10409                                callback = errorCallback = xhr.onload =
10410                                    xhr.onerror = xhr.onabort = xhr.ontimeout =
10411                                        xhr.onreadystatechange = null;
10412
10413                                if ( type === "abort" ) {
10414                                    xhr.abort();
10415                                } else if ( type === "error" ) {
10416
10417                                    // Support: IE <=9 only
10418                                    // On a manual native abort, IE9 throws
10419                                    // errors on any property access that is not readyState
10420                                    if ( typeof xhr.status !== "number" ) {
10421                                        complete( 0, "error" );
10422                                    } else {
10423                                        complete(
10424
10425                                            // File: protocol always yields status 0; see #8605, #14207
10426                                            xhr.status,
10427                                            xhr.statusText
10428                                        );
10429                                    }
10430                                } else {
10431                                    complete(
10432                                        xhrSuccessStatus[ xhr.status ] || xhr.status,
10433                                        xhr.statusText,
10434
10435                                        // Support: IE <=9 only
10436                                        // IE9 has no XHR2 but throws on binary (trac-11426)
10437                                        // For XHR2 non-text, let the caller handle it (gh-2498)
10438                                        ( xhr.responseType || "text" ) !== "text"  ||
10439                                        typeof xhr.responseText !== "string" ?
10440                                            { binary: xhr.response } :
10441                                            { text: xhr.responseText },
10442                                        xhr.getAllResponseHeaders()
10443                                    );
10444                                }
10445                            }
10446                        };
10447                    };
10448
10449                    // Listen to events
10450                    xhr.onload = callback();
10451                    errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
10452
10453                    // Support: IE 9 only
10454                    // Use onreadystatechange to replace onabort
10455                    // to handle uncaught aborts
10456                    if ( xhr.onabort !== undefined ) {
10457                        xhr.onabort = errorCallback;
10458                    } else {
10459                        xhr.onreadystatechange = function() {
10460
10461                            // Check readyState before timeout as it changes
10462                            if ( xhr.readyState === 4 ) {
10463
10464                                // Allow onerror to be called first,
10465                                // but that will not handle a native abort
10466                                // Also, save errorCallback to a variable
10467                                // as xhr.onerror cannot be accessed
10468                                window.setTimeout( function() {
10469                                    if ( callback ) {
10470                                        errorCallback();
10471                                    }
10472                                } );
10473                            }
10474                        };
10475                    }
10476
10477                    // Create the abort callback
10478                    callback = callback( "abort" );
10479
10480                    try {
10481
10482                        // Do send the request (this may raise an exception)
10483                        xhr.send( options.hasContent && options.data || null );
10484                    } catch ( e ) {
10485
10486                        // #14683: Only rethrow if this hasn't been notified as an error yet
10487                        if ( callback ) {
10488                            throw e;
10489                        }
10490                    }
10491                },
10492
10493                abort: function() {
10494                    if ( callback ) {
10495                        callback();
10496                    }
10497                }
10498            };
10499        }
10500    } );
10501
10502
10503
10504
10505// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
10506    jQuery.ajaxPrefilter( function( s ) {
10507        if ( s.crossDomain ) {
10508            s.contents.script = false;
10509        }
10510    } );
10511
10512// Install script dataType
10513    jQuery.ajaxSetup( {
10514        accepts: {
10515            script: "text/javascript, application/javascript, " +
10516                "application/ecmascript, application/x-ecmascript"
10517        },
10518        contents: {
10519            script: /\b(?:java|ecma)script\b/
10520        },
10521        converters: {
10522            "text script": function( text ) {
10523                jQuery.globalEval( text );
10524                return text;
10525            }
10526        }
10527    } );
10528
10529// Handle cache's special case and crossDomain
10530    jQuery.ajaxPrefilter( "script", function( s ) {
10531        if ( s.cache === undefined ) {
10532            s.cache = false;
10533        }
10534        if ( s.crossDomain ) {
10535            s.type = "GET";
10536        }
10537    } );
10538
10539// Bind script tag hack transport
10540    jQuery.ajaxTransport( "script", function( s ) {
10541
10542        // This transport only deals with cross domain or forced-by-attrs requests
10543        if ( s.crossDomain || s.scriptAttrs ) {
10544            var script, callback;
10545            return {
10546                send: function( _, complete ) {
10547                    script = jQuery( "<script>" )
10548                        .attr( s.scriptAttrs || {} )
10549                        .prop( { charset: s.scriptCharset, src: s.url } )
10550                        .on( "load error", callback = function( evt ) {
10551                            script.remove();
10552                            callback = null;
10553                            if ( evt ) {
10554                                complete( evt.type === "error" ? 404 : 200, evt.type );
10555                            }
10556                        } );
10557
10558                    // Use native DOM manipulation to avoid our domManip AJAX trickery
10559                    document.head.appendChild( script[ 0 ] );
10560                },
10561                abort: function() {
10562                    if ( callback ) {
10563                        callback();
10564                    }
10565                }
10566            };
10567        }
10568    } );
10569
10570
10571
10572
10573    var oldCallbacks = [],
10574        rjsonp = /(=)\?(?=&|$)|\?\?/;
10575
10576// Default jsonp settings
10577    jQuery.ajaxSetup( {
10578        jsonp: "callback",
10579        jsonpCallback: function() {
10580            var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) );
10581            this[ callback ] = true;
10582            return callback;
10583        }
10584    } );
10585
10586// Detect, normalize options and install callbacks for jsonp requests
10587    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
10588
10589        var callbackName, overwritten, responseContainer,
10590            jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
10591                    "url" :
10592                    typeof s.data === "string" &&
10593                    ( s.contentType || "" )
10594                        .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
10595                    rjsonp.test( s.data ) && "data"
10596            );
10597
10598        // Handle iff the expected data type is "jsonp" or we have a parameter to set
10599        if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
10600
10601            // Get callback name, remembering preexisting value associated with it
10602            callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
10603                s.jsonpCallback() :
10604                s.jsonpCallback;
10605
10606            // Insert callback into url or form data
10607            if ( jsonProp ) {
10608                s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
10609            } else if ( s.jsonp !== false ) {
10610                s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
10611            }
10612
10613            // Use data converter to retrieve json after script execution
10614            s.converters[ "script json" ] = function() {
10615                if ( !responseContainer ) {
10616                    jQuery.error( callbackName + " was not called" );
10617                }
10618                return responseContainer[ 0 ];
10619            };
10620
10621            // Force json dataType
10622            s.dataTypes[ 0 ] = "json";
10623
10624            // Install callback
10625            overwritten = window[ callbackName ];
10626            window[ callbackName ] = function() {
10627                responseContainer = arguments;
10628            };
10629
10630            // Clean-up function (fires after converters)
10631            jqXHR.always( function() {
10632
10633                // If previous value didn't exist - remove it
10634                if ( overwritten === undefined ) {
10635                    jQuery( window ).removeProp( callbackName );
10636
10637                    // Otherwise restore preexisting value
10638                } else {
10639                    window[ callbackName ] = overwritten;
10640                }
10641
10642                // Save back as free
10643                if ( s[ callbackName ] ) {
10644
10645                    // Make sure that re-using the options doesn't screw things around
10646                    s.jsonpCallback = originalSettings.jsonpCallback;
10647
10648                    // Save the callback name for future use
10649                    oldCallbacks.push( callbackName );
10650                }
10651
10652                // Call if it was a function and we have a response
10653                if ( responseContainer && isFunction( overwritten ) ) {
10654                    overwritten( responseContainer[ 0 ] );
10655                }
10656
10657                responseContainer = overwritten = undefined;
10658            } );
10659
10660            // Delegate to script
10661            return "script";
10662        }
10663    } );
10664
10665
10666
10667
10668// Support: Safari 8 only
10669// In Safari 8 documents created via document.implementation.createHTMLDocument
10670// collapse sibling forms: the second one becomes a child of the first one.
10671// Because of that, this security measure has to be disabled in Safari 8.
10672// https://bugs.webkit.org/show_bug.cgi?id=137337
10673    support.createHTMLDocument = ( function() {
10674        var body = document.implementation.createHTMLDocument( "" ).body;
10675        body.innerHTML = "<form></form><form></form>";
10676        return body.childNodes.length === 2;
10677    } )();
10678
10679
10680// Argument "data" should be string of html
10681// context (optional): If specified, the fragment will be created in this context,
10682// defaults to document
10683// keepScripts (optional): If true, will include scripts passed in the html string
10684    jQuery.parseHTML = function( data, context, keepScripts ) {
10685        if ( typeof data !== "string" ) {
10686            return [];
10687        }
10688        if ( typeof context === "boolean" ) {
10689            keepScripts = context;
10690            context = false;
10691        }
10692
10693        var base, parsed, scripts;
10694
10695        if ( !context ) {
10696
10697            // Stop scripts or inline event handlers from being executed immediately
10698            // by using document.implementation
10699            if ( support.createHTMLDocument ) {
10700                context = document.implementation.createHTMLDocument( "" );
10701
10702                // Set the base href for the created document
10703                // so any parsed elements with URLs
10704                // are based on the document's URL (gh-2965)
10705                base = context.createElement( "base" );
10706                base.href = document.location.href;
10707                context.head.appendChild( base );
10708            } else {
10709                context = document;
10710            }
10711        }
10712
10713        parsed = rsingleTag.exec( data );
10714        scripts = !keepScripts && [];
10715
10716        // Single tag
10717        if ( parsed ) {
10718            return [ context.createElement( parsed[ 1 ] ) ];
10719        }
10720
10721        parsed = buildFragment( [ data ], context, scripts );
10722
10723        if ( scripts && scripts.length ) {
10724            jQuery( scripts ).remove();
10725        }
10726
10727        return jQuery.merge( [], parsed.childNodes );
10728    };
10729
10730
10731    /**
10732     * Load a url into a page
10733     */
10734    jQuery.fn.load = function( url, params, callback ) {
10735        var selector, type, response,
10736            self = this,
10737            off = url.indexOf( " " );
10738
10739        if ( off > -1 ) {
10740            selector = stripAndCollapse( url.slice( off ) );
10741            url = url.slice( 0, off );
10742        }
10743
10744        // If it's a function
10745        if ( isFunction( params ) ) {
10746
10747            // We assume that it's the callback
10748            callback = params;
10749            params = undefined;
10750
10751            // Otherwise, build a param string
10752        } else if ( params && typeof params === "object" ) {
10753            type = "POST";
10754        }
10755
10756        // If we have elements to modify, make the request
10757        if ( self.length > 0 ) {
10758            jQuery.ajax( {
10759                url: url,
10760
10761                // If "type" variable is undefined, then "GET" method will be used.
10762                // Make value of this field explicit since
10763                // user can override it through ajaxSetup method
10764                type: type || "GET",
10765                dataType: "html",
10766                data: params
10767            } ).done( function( responseText ) {
10768
10769                // Save response for use in complete callback
10770                response = arguments;
10771
10772                self.html( selector ?
10773
10774                    // If a selector was specified, locate the right elements in a dummy div
10775                    // Exclude scripts to avoid IE 'Permission Denied' errors
10776                    jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
10777
10778                    // Otherwise use the full result
10779                    responseText );
10780
10781                // If the request succeeds, this function gets "data", "status", "jqXHR"
10782                // but they are ignored because response was set above.
10783                // If it fails, this function gets "jqXHR", "status", "error"
10784            } ).always( callback && function( jqXHR, status ) {
10785                self.each( function() {
10786                    callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
10787                } );
10788            } );
10789        }
10790
10791        return this;
10792    };
10793
10794
10795
10796
10797    jQuery.expr.pseudos.animated = function( elem ) {
10798        return jQuery.grep( jQuery.timers, function( fn ) {
10799            return elem === fn.elem;
10800        } ).length;
10801    };
10802
10803
10804
10805
10806    jQuery.offset = {
10807        setOffset: function( elem, options, i ) {
10808            var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
10809                position = jQuery.css( elem, "position" ),
10810                curElem = jQuery( elem ),
10811                props = {};
10812
10813            // Set position first, in-case top/left are set even on static elem
10814            if ( position === "static" ) {
10815                elem.style.position = "relative";
10816            }
10817
10818            curOffset = curElem.offset();
10819            curCSSTop = jQuery.css( elem, "top" );
10820            curCSSLeft = jQuery.css( elem, "left" );
10821            calculatePosition = ( position === "absolute" || position === "fixed" ) &&
10822                ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
10823
10824            // Need to be able to calculate position if either
10825            // top or left is auto and position is either absolute or fixed
10826            if ( calculatePosition ) {
10827                curPosition = curElem.position();
10828                curTop = curPosition.top;
10829                curLeft = curPosition.left;
10830
10831            } else {
10832                curTop = parseFloat( curCSSTop ) || 0;
10833                curLeft = parseFloat( curCSSLeft ) || 0;
10834            }
10835
10836            if ( isFunction( options ) ) {
10837
10838                // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
10839                options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
10840            }
10841
10842            if ( options.top != null ) {
10843                props.top = ( options.top - curOffset.top ) + curTop;
10844            }
10845            if ( options.left != null ) {
10846                props.left = ( options.left - curOffset.left ) + curLeft;
10847            }
10848
10849            if ( "using" in options ) {
10850                options.using.call( elem, props );
10851
10852            } else {
10853                curElem.css( props );
10854            }
10855        }
10856    };
10857
10858    jQuery.fn.extend( {
10859
10860        // offset() relates an element's border box to the document origin
10861        offset: function( options ) {
10862
10863            // Preserve chaining for setter
10864            if ( arguments.length ) {
10865                return options === undefined ?
10866                    this :
10867                    this.each( function( i ) {
10868                        jQuery.offset.setOffset( this, options, i );
10869                    } );
10870            }
10871
10872            var rect, win,
10873                elem = this[ 0 ];
10874
10875            if ( !elem ) {
10876                return;
10877            }
10878
10879            // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
10880            // Support: IE <=11 only
10881            // Running getBoundingClientRect on a
10882            // disconnected node in IE throws an error
10883            if ( !elem.getClientRects().length ) {
10884                return { top: 0, left: 0 };
10885            }
10886
10887            // Get document-relative position by adding viewport scroll to viewport-relative gBCR
10888            rect = elem.getBoundingClientRect();
10889            win = elem.ownerDocument.defaultView;
10890            return {
10891                top: rect.top + win.pageYOffset,
10892                left: rect.left + win.pageXOffset
10893            };
10894        },
10895
10896        // position() relates an element's margin box to its offset parent's padding box
10897        // This corresponds to the behavior of CSS absolute positioning
10898        position: function() {
10899            if ( !this[ 0 ] ) {
10900                return;
10901            }
10902
10903            var offsetParent, offset, doc,
10904                elem = this[ 0 ],
10905                parentOffset = { top: 0, left: 0 };
10906
10907            // position:fixed elements are offset from the viewport, which itself always has zero offset
10908            if ( jQuery.css( elem, "position" ) === "fixed" ) {
10909
10910                // Assume position:fixed implies availability of getBoundingClientRect
10911                offset = elem.getBoundingClientRect();
10912
10913            } else {
10914                offset = this.offset();
10915
10916                // Account for the *real* offset parent, which can be the document or its root element
10917                // when a statically positioned element is identified
10918                doc = elem.ownerDocument;
10919                offsetParent = elem.offsetParent || doc.documentElement;
10920                while ( offsetParent &&
10921                ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
10922                jQuery.css( offsetParent, "position" ) === "static" ) {
10923
10924                    offsetParent = offsetParent.parentNode;
10925                }
10926                if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
10927
10928                    // Incorporate borders into its offset, since they are outside its content origin
10929                    parentOffset = jQuery( offsetParent ).offset();
10930                    parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
10931                    parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
10932                }
10933            }
10934
10935            // Subtract parent offsets and element margins
10936            return {
10937                top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
10938                left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
10939            };
10940        },
10941
10942        // This method will return documentElement in the following cases:
10943        // 1) For the element inside the iframe without offsetParent, this method will return
10944        //    documentElement of the parent window
10945        // 2) For the hidden or detached element
10946        // 3) For body or html element, i.e. in case of the html node - it will return itself
10947        //
10948        // but those exceptions were never presented as a real life use-cases
10949        // and might be considered as more preferable results.
10950        //
10951        // This logic, however, is not guaranteed and can change at any point in the future
10952        offsetParent: function() {
10953            return this.map( function() {
10954                var offsetParent = this.offsetParent;
10955
10956                while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
10957                    offsetParent = offsetParent.offsetParent;
10958                }
10959
10960                return offsetParent || documentElement;
10961            } );
10962        }
10963    } );
10964
10965// Create scrollLeft and scrollTop methods
10966    jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
10967        var top = "pageYOffset" === prop;
10968
10969        jQuery.fn[ method ] = function( val ) {
10970            return access( this, function( elem, method, val ) {
10971
10972                // Coalesce documents and windows
10973                var win;
10974                if ( isWindow( elem ) ) {
10975                    win = elem;
10976                } else if ( elem.nodeType === 9 ) {
10977                    win = elem.defaultView;
10978                }
10979
10980                if ( val === undefined ) {
10981                    return win ? win[ prop ] : elem[ method ];
10982                }
10983
10984                if ( win ) {
10985                    win.scrollTo(
10986                        !top ? val : win.pageXOffset,
10987                        top ? val : win.pageYOffset
10988                    );
10989
10990                } else {
10991                    elem[ method ] = val;
10992                }
10993            }, method, val, arguments.length );
10994        };
10995    } );
10996
10997// Support: Safari <=7 - 9.1, Chrome <=37 - 49
10998// Add the top/left cssHooks using jQuery.fn.position
10999// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
11000// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
11001// getComputedStyle returns percent when specified for top/left/bottom/right;
11002// rather than make the css module depend on the offset module, just check for it here
11003    jQuery.each( [ "top", "left" ], function( _i, prop ) {
11004        jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
11005            function( elem, computed ) {
11006                if ( computed ) {
11007                    computed = curCSS( elem, prop );
11008
11009                    // If curCSS returns percentage, fallback to offset
11010                    return rnumnonpx.test( computed ) ?
11011                        jQuery( elem ).position()[ prop ] + "px" :
11012                        computed;
11013                }
11014            }
11015        );
11016    } );
11017
11018
11019// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
11020    jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
11021        jQuery.each( {
11022            padding: "inner" + name,
11023            content: type,
11024            "": "outer" + name
11025        }, function( defaultExtra, funcName ) {
11026
11027            // Margin is only for outerHeight, outerWidth
11028            jQuery.fn[ funcName ] = function( margin, value ) {
11029                var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
11030                    extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
11031
11032                return access( this, function( elem, type, value ) {
11033                    var doc;
11034
11035                    if ( isWindow( elem ) ) {
11036
11037                        // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
11038                        return funcName.indexOf( "outer" ) === 0 ?
vendor: 5,067 bytes, lines 11039-11196
11039                            elem[ "inner" + name ] :
11040                            elem.document.documentElement[ "client" + name ];
11041                    }
11042
11043                    // Get document width or height
11044                    if ( elem.nodeType === 9 ) {
11045                        doc = elem.documentElement;
11046
11047                        // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
11048                        // whichever is greatest
11049                        return Math.max(
11050                            elem.body[ "scroll" + name ], doc[ "scroll" + name ],
11051                            elem.body[ "offset" + name ], doc[ "offset" + name ],
11052                            doc[ "client" + name ]
11053                        );
11054                    }
11055
11056                    return value === undefined ?
11057
11058                        // Get width or height on the element, requesting but not forcing parseFloat
11059                        jQuery.css( elem, type, extra ) :
11060
11061                        // Set width or height on the element
11062                        jQuery.style( elem, type, value, extra );
11063                }, type, chainable ? margin : undefined, chainable );
11064            };
11065        } );
11066    } );
11067
11068
11069    jQuery.each( [
11070        "ajaxStart",
11071        "ajaxStop",
11072        "ajaxComplete",
11073        "ajaxError",
11074        "ajaxSuccess",
11075        "ajaxSend"
11076    ], function( _i, type ) {
11077        jQuery.fn[ type ] = function( fn ) {
11078            return this.on( type, fn );
11079        };
11080    } );
11081
11082
11083
11084
11085    jQuery.fn.extend( {
11086
11087        bind: function( types, data, fn ) {
11088            return this.on( types, null, data, fn );
11089        },
11090        unbind: function( types, fn ) {
11091            return this.off( types, null, fn );
11092        },
11093
11094        delegate: function( selector, types, data, fn ) {
11095            return this.on( types, selector, data, fn );
11096        },
11097        undelegate: function( selector, types, fn ) {
11098
11099            // ( namespace ) or ( selector, types [, fn] )
11100            return arguments.length === 1 ?
11101                this.off( selector, "**" ) :
11102                this.off( types, selector || "**", fn );
11103        },
11104
11105        hover: function( fnOver, fnOut ) {
11106            return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
11107        }
11108    } );
11109
11110    jQuery.each(
11111        ( "blur focus focusin focusout resize scroll click dblclick " +
11112            "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
11113            "change select submit keydown keypress keyup contextmenu" ).split( " " ),
11114        function( _i, name ) {
11115
11116            // Handle event binding
11117            jQuery.fn[ name ] = function( data, fn ) {
11118                return arguments.length > 0 ?
11119                    this.on( name, null, data, fn ) :
11120                    this.trigger( name );
11121            };
11122        }
11123    );
11124
11125
11126
11127
11128// Support: Android <=4.0 only
11129// Make sure we trim BOM and NBSP
11130    var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
11131
11132// Bind a function to a context, optionally partially applying any
11133// arguments.
11134// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
11135// However, it is not slated for removal any time soon
11136    jQuery.proxy = function( fn, context ) {
11137        var tmp, args, proxy;
11138
11139        if ( typeof context === "string" ) {
11140            tmp = fn[ context ];
11141            context = fn;
11142            fn = tmp;
11143        }
11144
11145        // Quick check to determine if target is callable, in the spec
11146        // this throws a TypeError, but we will just return undefined.
11147        if ( !isFunction( fn ) ) {
11148            return undefined;
11149        }
11150
11151        // Simulated bind
11152        args = slice.call( arguments, 2 );
11153        proxy = function() {
11154            return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
11155        };
11156
11157        // Set the guid of unique handler to the same of original handler, so it can be removed
11158        proxy.guid = fn.guid = fn.guid || jQuery.guid++;
11159
11160        return proxy;
11161    };
11162
11163    jQuery.holdReady = function( hold ) {
11164        if ( hold ) {
11165            jQuery.readyWait++;
11166        } else {
11167            jQuery.ready( true );
11168        }
11169    };
11170    jQuery.isArray = Array.isArray;
11171    jQuery.parseJSON = JSON.parse;
11172    jQuery.nodeName = nodeName;
11173    jQuery.isFunction = isFunction;
11174    jQuery.isWindow = isWindow;
11175    jQuery.camelCase = camelCase;
11176    jQuery.type = toType;
11177
11178    jQuery.now = Date.now;
11179
11180    jQuery.isNumeric = function( obj ) {
11181
11182        // As of jQuery 3.0, isNumeric is limited to
11183        // strings and numbers (primitives or objects)
11184        // that can be coerced to finite numbers (gh-2662)
11185        var type = jQuery.type( obj );
11186        return ( type === "number" || type === "string" ) &&
11187
11188            // parseFloat NaNs numeric-cast false positives ("")
11189            // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
11190            // subtraction forces infinities to NaN
11191            !isNaN( obj - parseFloat( obj ) );
11192    };
11193
11194    jQuery.trim = function( text ) {
11195        return text == null ?
11196            "" :
11197            ( text + "" ).replace( rtrim, "" );
11198    };
11199
11200
11201
11202// Register as a named AMD module, since jQuery can be concatenated with other
11203// files that may use define, but not via a proper concatenation script that
11204// understands anonymous AMD modules. A named AMD is safest and most robust
11205// way to register. Lowercase jquery is used because AMD module names are
11206// derived from file names, and jQuery is normally delivered in a lowercase
11207// file name. Do this after creating the global so that if an AMD module wants
11208// to call noConflict to hide this version of jQuery, it will work.
11209
11210// Note that for maximum portability, libraries that are not jQuery should
11211// declare themselves as anonymous modules, and avoid setting a global if an
11212// AMD loader is present. jQuery is a special case. For more information, see
11213// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
11214
11215    if ( typeof define === "function" && define.amd ) {
11216        define( "jquery", [], function() {
11217            return jQuery;
11218        } );
11219    }
11220
11221
11222
11223
11224    var
11225
11226        // Map over jQuery in case of overwrite
11227        _jQuery = window.jQuery,
11228
11229        // Map over the $ in case of overwrite
11230        _$ = window.$;
11231
11232    jQuery.noConflict = function( deep ) {
11233        if ( window.$ === jQuery ) {
11234            window.$ = _$;
11235        }
11236
11237        if ( deep && window.jQuery === jQuery ) {
11238            window.jQuery = _jQuery;
11239        }
11240
11241        return jQuery;
11242    };
11243
11244// Expose jQuery and $ identifiers, even in AMD
11245// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
11246// and CommonJS for browser emulators (#13566)
11247    if ( typeof noGlobal === "undefined" ) {
11248        window.jQuery = window.$ = jQuery;
11249    }
11250
11251
11252
11253
11254    return jQuery;
11255} );
11256/**
11257 * Cookie plugin
11258 *
11259 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
11260 * Dual licensed under the MIT and GPL licenses:
11261 * http://www.opensource.org/licenses/mit-license.php
11262 * http://www.gnu.org/licenses/gpl.html
11263 *
11264 */
11265
11266/**
11267 * Create a cookie with the given name and value and other optional parameters.
11268 *
11269 * @example $.cookie('the_cookie', 'the_value');
11270 * @desc Set the value of a cookie.
11271 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
11272 * @desc Create a cookie with all available options.
11273 * @example $.cookie('the_cookie', 'the_value');
11274 * @desc Create a session cookie.
11275 * @example $.cookie('the_cookie', null);
11276 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
11277 *       used when the cookie was set.
11278 *
11279 * @param String name The name of the cookie.
11280 * @param String value The value of the cookie.
11281 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
11282 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
11283 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
11284 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
11285 *                             when the the browser exits.
11286 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
11287 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
11288 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
11289 *                        require a secure protocol (like HTTPS).
11290 * @type undefined
11291 *
11292 * @name $.cookie
11293 * @cat Plugins/Cookie
11294 * @author Klaus Hartl/[email protected]
11295 */
11296
11297/**
11298 * Get the value of a cookie with the given name.
11299 *
11300 * @example $.cookie('the_cookie');
11301 * @desc Get the value of a cookie.
11302 *
11303 * @param String name The name of the cookie.
11304 * @return The value of the cookie.
11305 * @type String
11306 *
11307 * @name $.cookie
11308 * @cat Plugins/Cookie
11309 * @author Klaus Hartl/[email protected]
11310 */
11311jQuery.cookie = function(name, value, options) {
11312    if (typeof value != 'undefined') { // name and value given, set cookie
11313        options = options || {};
11314        if (value === null) {
11315            value = '';
11316            options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
11317            options.expires = -1;
11318        }
11319        var expires = '';
11320        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
11321            var date;
11322            if (typeof options.expires == 'number') {
11323                date = new Date();
11324                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
11325            } else {
11326                date = options.expires;
11327            }
11328            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
11329        }
11330        // NOTE Needed to parenthesize options.path and options.domain
11331        // in the following expressions, otherwise they evaluate to undefined
11332        // in the packed version for some reason...
11333        var path = options.path ? '; path=' + (options.path) : '';
11334        var domain = options.domain ? '; domain=' + (options.domain) : '';
11335        var secure = options.secure ? '; secure' : '';
11336        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
11337    } else { // only name given, get cookie
11338        var cookieValue = null;
11339        if (document.cookie && document.cookie != '') {
11340            var cookies = document.cookie.split(';');
11341            for (var i = 0; i < cookies.length; i++) {
11342                var cookie = jQuery.trim(cookies[i]);
11343                // Does this cookie string begin with the name we want?
11344                if (cookie.substring(0, name.length + 1) == (name + '=')) {
11345                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
11346                    break;
11347                }
11348            }
11349        }
11350        return cookieValue;
11351    }
11352};
11353// JQuery URL Parser plugin - https://github.com/allmarkedup/jQuery-URL-Parser
11354// Written by Mark Perkins, [email protected]
11355// License: http://unlicense.org/ (i.e. do what you want with it!)
11356
11357;(function($) {
11358
11359    var tag2attr = {
11360        a       : 'href',
11361        img     : 'src',
11362        form    : 'action',
11363        base    : 'href',
11364        script  : 'src',
11365        iframe  : 'src',
11366        link    : 'href'
11367    },
11368
11369    key = ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"], // keys available to query
11370
11371    aliases = { "anchor" : "fragment" }, // aliases for backwards compatability
11372
11373    parser = {
11374        strict  : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
11375        loose   :  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
11376    },
11377
11378    querystring_parser = /(?:^|&|;)([^&=;]*)=?([^&;]*)/g, // supports both ampersand and semicolon-delimted query string key/value pairs
11379
11380    fragment_parser = /(?:^|&|;)([^&=;]*)=?([^&;]*)/g; // supports both ampersand and semicolon-delimted fragment key/value pairs
11381
11382    function parseUri( url, strictMode )
11383    {
11384        var str = decodeURI( url ),
11385            res   = parser[ strictMode || false ? "strict" : "loose" ].exec( str ),
11386            uri = { attr : {}, param : {}, seg : {} },
11387            i   = 14;
11388
11389        while ( i-- )
11390        {
11391            uri.attr[ key[i] ] = res[i] || "";
11392        }
11393
11394        // build query and fragment parameters
11395
11396        uri.param['query'] = {};
11397        uri.param['fragment'] = {};
11398
11399        uri.attr['query'].replace( querystring_parser, function ( $0, $1, $2 ){
11400            if ($1)
11401            {
11402                uri.param['query'][$1] = $2;
11403            }
11404        });
11405
11406        uri.attr['fragment'].replace( fragment_parser, function ( $0, $1, $2 ){
11407            if ($1)
11408            {
11409                uri.param['fragment'][$1] = $2;
11410            }
11411        });
11412
11413        // split path and fragement into segments
11414
11415        uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/');
11416
11417        uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/');
11418
11419        // compile a 'base' domain attribute
11420
11421        uri.attr['base'] = uri.attr.host ? uri.attr.protocol+"://"+uri.attr.host + (uri.attr.port ? ":"+uri.attr.port : '') : '';
11422
11423        return uri;
11424    };
11425
11426    function getAttrName( elm )
11427    {
11428        var tn = elm.tagName;
11429        if ( tn !== undefined ) return tag2attr[tn.toLowerCase()];
11430        return tn;
11431    }
11432
11433    $.fn.url = function( strictMode )
11434    {
11435        var url = '';
11436
11437        if ( this.length )
11438        {
11439            url = $(this).attr( getAttrName(this[0]) ) || '';
11440        }
11441
11442        return $.url({ url : url, strict : strictMode });
11443    };
11444
11445    $.url = function( opts )
11446    {
11447        var url     = '',
11448            strict  = false;
11449
11450        if ( typeof opts === 'string' )
11451        {
11452            url = opts;
11453        }
11454        else
11455        {
11456            opts = opts || {};
11457            strict = opts.strict || strict;
11458            url = opts.url === undefined ? window.location.toString() : opts.url;
11459        }
11460
11461        return {
11462
11463            data : parseUri(url, strict),
11464
11465            // get various attributes from the URI
11466            attr : function( attr )
11467            {
11468                attr = aliases[attr] || attr;
11469                return attr !== undefined ? this.data.attr[attr] : this.data.attr;
11470            },
11471
11472            // return query string parameters
11473            param : function( param )
11474            {
11475                return param !== undefined ? this.data.param.query[param] : this.data.param.query;
11476            },
11477
11478            // return fragment parameters
11479            fparam : function( param )
11480            {
11481                return param !== undefined ? this.data.param.fragment[param] : this.data.param.fragment;
11482            },
11483
11484            // return path segments
11485            segment : function( seg )
11486            {
11487                if ( seg === undefined )
11488                {
11489                    return this.data.seg.path;
11490                }
11491                else
11492                {
11493                    seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
11494                    return this.data.seg.path[seg];
11495                }
11496            },
11497
11498            // return fragment segments
11499            fsegment : function( seg )
11500            {
11501                if ( seg === undefined )
11502                {
11503                    return this.data.seg.fragment;
11504                }
11505                else
11506                {
11507                    seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
11508                    return this.data.seg.fragment[seg];
11509                }
11510            },
11511
11512            replace : function( param, value )
11513            {
11514                var sParamPattern = eval('/(&|\\?)'+param+'=[a-zA-Z0-9\_\-\|\+\^\#\@\$\%\*\/\;\:]+/');
11515                var sIsParamPattern = /\?[a-zA-Z0-9_-]+/;
11516                if (sIsParamPattern.test(url) === true) {
11517                    if (sParamPattern.test(url) === true) return url.replace(sParamPattern, '$1'+param+'='+value);
11518                    return url + '&'+param+'='+value;
11519                } else {
11520                    if (sParamPattern.test(url) === true) return url.replace(sParamPattern, '$1'+param+'='+value);
11521                    return url + '?'+param+'='+value;
11522                }
11523
11524            }
11525
11526        };
11527
11528    };
11529
11530})(jQuery);
11531(function($) {
11532    $.fn.setValue = function(value) {
11533        if ($(this).is('SELECT') === true) {
11534            $(this).find('option[value="'+ value +'"]').prop('selected', true);
11535        } else {
11536            $(this).val(value);
11537        }
11538    };
11539})(jQuery);
11540var EC$ = jQuery.noConflict(true);
11541/*!
11542 * jQuery JavaScript Library v1.4.4
11543 * http://jquery.com/
11544 *
11545 * Copyright 2010, John Resig
11546 * Dual licensed under the MIT or GPL Version 2 licenses.
11547 * http://jquery.org/license
11548 *
11549 * Includes Sizzle.js
11550 * http://sizzlejs.com/
11551 * Copyright 2010, The Dojo Foundation
11552 * Released under the MIT, BSD, and GPL Licenses.
11553 *
11554 * Date: Thu Nov 11 19:04:53 2010 -0500
11555 */
11556(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
11557h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
11558h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
11559"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
11560e);
11560if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
11561"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
11562a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
11563C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
11564s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
11565j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
11566toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
11567-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
11568if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(s==="__proto__")continue;if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
11569if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
11570b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
11571!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
11572l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
11573z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
11574s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="str
11574ing"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
11575s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
11576[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
11577false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
11578k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
11579scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
11580false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
115811;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
11582"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
11583c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
11584else if(d)delete f[e];else for(var l in a)delete a[l]}}
11584,acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
11585a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
11586c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
11587a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
11588colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
115891)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
11590l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
11591"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
11592if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
11593a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});
11593if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
11594attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
11595b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
11596c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
11597arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
11598d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
11599c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
11600w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;
11600if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
116018)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
11602"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
11603d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
11604fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
11605d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
11606Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
11607c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
11608var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};
vendor: 8,894 bytes, lines 11608-11625
11608c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
11609"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
11610xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
11611B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
11612"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
116130&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
11614a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
116151;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
11616"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
11617c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
11618(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
11619[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
11620break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
11621q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
11622l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
11623return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
11624B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
11625POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}}
vendor: 3,377 bytes, lines 11625-11632
11625,relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
11626i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
11627i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
11628"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
11629m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
11630true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
11631g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
116320===i},eq:function(g,i,n){return n[3]-0===i}}
11632,filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
11633"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
11634i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
11635if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
11636g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
11637for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
11638i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
11639n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
11640function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
11641p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");
11641if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
11642t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
11643function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
11644c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
11645not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
11646h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
11647c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
116482,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
11649b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
11650e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
11651"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];
vendor: 4,442 bytes, lines 11651-11660
11651c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
11652c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
11653wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
11654prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
11655this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
11656return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
11657else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
11658c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
11659b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
11660this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;
11660if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
11661prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
11662b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
116631;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
11664d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
11665jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
11666zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
11667h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
11668if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);
11668if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
11669d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
11670e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
11671ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
11672"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
11673!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
11674getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
11675script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
11676!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
11677false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");
11677if(b.scriptCharset)A.charset=b.scriptCharset;
11678A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
11679b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
11680c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
11681c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
11682encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
11683[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
11684e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
11685if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["o
11685pacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
116863),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
11687d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
11688d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
11689"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
116901)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
11691d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
11692Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
11693var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
11694this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;
11694if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
11695this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
11696c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
11697b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
11698h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
11699for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
11700parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
11701height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
11702f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
11703"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
11704e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;
11704return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
11705c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
11706c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
11707b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
11708
11709(function($, jQuery) {
11710    // .uuid 복원
11711    if (typeof jQuery.uuid == 'undefined') {
11712        jQuery.uuid = 0;
11713    }
11714
11715    // .curCSS 복원
11716    if ( !jQuery.curCSS ) {
11717        jQuery.curCSS = jQuery.css;
11718    }
11719
11720    // .browser 복원
11721    if ( !$.browser ) {
11722        $.uaMatch = function( ua ) {
11723            ua = ua.toLowerCase();
11724
11725            var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
11726                /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
11727                /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
11728                /(msie) ([\w.]+)/.exec( ua ) ||
11729                ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
11730                [];
11731
11732            return {
11733                browser: match[ 1 ] || "",
11734                version: match[ 2 ] || "0"
11735            };
11736        };
11737
11738        matched = $.uaMatch( navigator.userAgent );
11739        browser = {};
11740
11741        if ( matched.browser ) {
11742            browser[ matched.browser ] = true;
11743            browser.version = matched.version;
11744        }
11745
11746        // Chrome is Webkit, but Webkit is also Safari.
11747        if ( browser.chrome ) {
11748            browser.webkit = true;
11749        } else if ( browser.webkit ) {
11750            browser.safari = true;
11751        }
11752
11753        $.browser = browser;
11754    }
11755
11756    // $.escapeSelector Polyfill
11757    if ( !$.escapeSelector ) {
11758        $.escapeSelector = function( sel ) {
11759            var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g;
11760            var fcssescape = function( ch, asCodePoint ) {
11761                if ( asCodePoint ) {
11762
11763                    // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
11764                    if ( ch === "\0" ) {
11765                        return "\uFFFD";
11766                    }
11767
11768                    // Control characters and (dependent upon position) numbers get escaped as code points
11769                    return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
11770                }
11771
11772                // Other potentially-special ASCII characters get backslash-escaped
11773                return "\\" + ch;
11774            };
11775
11776            return ( sel + "" ).replace( rcssescape, fcssescape );
11777        };
11778    }
11779})($, jQuery);
11780(function(global) {if (global.moment) {global.moment_original = global.moment;}
vendor: 58,852 bytes, line 11781
11781!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var H;function _(){return H.apply(null,arguments)}function y(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 g(e){return void 0===e}function w(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 Wt(e,t,n,s,!0).utc()}function p(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){var t,n,s=e._d&&!isNaN(e._d.getTime());return s&&(t=p(e),n=j.call(t.parsedDateParts,function(e){return null!=e}),s=t.overflow<0&&!t.empty&&!t.invalidEra&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n),e._strict)&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e)?s:(e._isValid=s,e._isValid)}function I(e){var t=l(NaN);return null!=e?E(p(t),e):p(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=_.momentProperties=[],z=!1;function q(e,t){var n,s,i,r=Z.length;if(g(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),g(t._i)||(e._i=t._i),g(t._f)||(e._f=t._f),g(t._l)||(e._l=t._l),g(t._strict)||(e._strict=t._strict),g(t._tzm)||(e._tzm=t._tzm),g(t._isUTC)||(e._isUTC=t._isUTC),g(t._offset)||(e._offset=t._offset),g(t._pf)||(e._pf=p(t)),g(t._locale)||(e._locale=t._locale),0<r)for(n=0;n<r;n++)g(i=t[s=Z[n]])||(e[s]=i);return e}function $(e){q(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===z&&(z=!0,_.updateOffset(this),z=!1)}function k(e){return e instanceof $||null!=e&&null!=e._isAMomentObject}function B(e){!1===_.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function e(r,a){var o=!0;return E(function(){if(null!=_.deprecationHandler&&_.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!=_.deprecationHandler&&_.deprecationHandler(e,t),J[e]||(B(t),J[e]=!0)}function a(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)}_.suppressDeprecationWarnings=!1,_.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+=a(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={D:"date",dates:"date",date:"date",d:"day",days:"day",day:"day",e:"weekday",weekdays:"weekday",weekday:"weekday",E:"isoWeekday",isoweekdays:"isoWeekday",isoweekday:"isoWeekday",DDD:"dayOfYear",dayofyears:"dayOfYear",dayofyear:"dayOfYear",h:"hour",hours:"hour",hour:"hour",ms:"millisecond",milliseconds:"millisecond",millisecond:"millisecond",m:"minute",minutes:"minute",minute:"minute",M:"month",months:"month",month:"month",Q:"quarter",quarters:"quarter",quarter:"quarter",s:"second",seconds:"second",second:"second",gg:"weekYear",weekyears:"weekYear",weekyear:"weekYear",GG:"isoWeekYear",isoweekyears:"isoWeekYear",isoweekyear:"isoWeekYear",w:"week",weeks:"week",week:"week",W:"isoWeek",isoweeks:"isoWeek",isoweek:"isoWeek",y:"year",years:"year",year:"year"};function o(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=o(n))&&(s[t]=e[n]);return s}var le={date:9,day:11,weekday:11,isoWeekday:11,dayOfYear:4,hour:13,millisecond:16,minute:14,month:8,quarter:7,second:15,weekYear:1,isoWeekYear:1,week:5,isoWeek:5,year:1};var de=/\d/,t=/\d\d/,he=/\d{3}/,ce=/\d{4}/,fe=/[+-]?\d{6}/,n=/\d\d?/,me=/\d\d\d\d?/,_e=/\d\d\d\d\d\d?/,ye=/\d{1,3}/,ge=/\d{1,4}/,we=/[+-]?\d{1,6}/,pe=/\d+/,ke=/[+-]?\d+/,Me=/Z|[+-]\d\d:?\d\d/gi,ve=/Z|[+-]\d\d(?::?\d\d)?/gi,i=/[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,u=/^[1-9]\d?/,d=/^([1-9]\d|\d)/;function h(e,n,s){Ye[e]=a(n)?n:function(e,t){return e&&s?s:n}}function De(e,t){return c(Ye,e)?Ye[e](t._strict,t._locale):new RegExp(f(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function f(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function m(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function M(e){var e=+e,t=0;return t=0!=e&&isFinite(e)?m(e):t}var Ye={},Se={};function v(e,n){var t,s,i=n;for("string"==typeof e&&(e=[e]),w(n)&&(i=function(e,t){t[n]=M(e)}),s=e.length,t=0;t<s;t++)Se[e[t]]=i}function Oe(e,i){v(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}function be(e){return e%4==0&&e%100!=0||e%400==0}var D=0,Y=1,S=2,O=3,b=4,T=5,Te=6,xe=7,Ne=8;function We(e){return be(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"),h("Y",ke),h("YY",n,t),h("YYYY",ge,ce),h("YYYYY",we,fe),h("YYYYYY",we,fe),v(["YYYYY","YYYYYY"],D),v("YYYY",function(e,t){t[D]=2===e.length?_.parseTwoDigitYear(e):M(e)}),v("YY",function(e,t){t[D]=_.parseTwoDigitYear(e)}),v("Y",function(e,t){t[D]=parseInt(e,10)}),_.parseTwoDigitYear=function(e){return M(e)+(68<M(e)?1900:2e3)};var x,Pe=Re("FullYear",!0);function Re(t,n){return function(e){return null!=e?(Ue(this,t,e),_.updateOffset(this,n),this):Ce(this,t)}}function Ce(e,t){if(!e.isValid())return NaN;var n=e._d,s=e._isUTC;switch(t){case"Milliseconds":return s?n.getUTCMilliseconds():n.getMilliseconds();case"Seconds":return s?n.getUTCSeconds():n.getSeconds();case"Minutes":return s?n.getUTCMinutes():n.getMinutes();case"Hours":return s?n.getUTCHours():n.getHours();case"Date":return s?n.getUTCDate():n.getDate();case"Day":return s?n.getUTCDay():n.getDay();case"Month":return s?n.getUTCMonth():n.getMonth();case"FullYear":return s?n.getUTCFullYear():n.getFullYear();default:return NaN}}function Ue(e,t,n){var s,i,r;if(e.isValid()&&!isNaN(n)){switch(s=e._d,i=e._isUTC,t){case"Milliseconds":return i?s.setUTCMilliseconds(n):s.setMilliseconds(n);case"Seconds":return i?s.setUTCSeconds(n):s.setSeconds(n);case"Minutes":return i?s.setUTCMinutes(n):s.setMinutes(n);case"Hours":return i?s.setUTCHours(n):s.setHours(n);case"Date":return i?s.setUTCDate(n):s.setDate(n);case"FullYear":break;default:return}t=n,r=e.month(),e=29!==(e=e.date())||1!==r||be(t)?e:28,i?s.setUTCFullYear(t,r,e):s.setFullYear(t,r,e)}}function He(e,t){var n;return isNaN(e)||isNaN(t)?NaN:(n=(t%(n=12)+n)%n,e+=(t-n)/12,1==n?be(e)?29:28:31-n%7%2)}x=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)}),h("M",n,u),h("MM",n,t),h("MMM",function(e,t){return t.monthsShortRegex(e)}),h("MMMM",function(e,t){return t.monthsRegex(e)}),v(["M","MM"],function(e,t){t[Y]=M(e)-1}),v(["MMM","MMMM"],function(e,t,n,s){s=n._locale.monthsParse(e,s,n._strict);null!=s?t[Y]=s:p(n).invalidMonth=e});var Fe="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Le="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),Ve=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Ge=i,Ee=i;function Ae(e,t){if(e.isValid()){if("string"==typeof t)if(/^\d+$/.test(t))t=M(t);else if(!w(t=e.localeData().monthsParse(t)))return;var n=(n=e.date())<29?n:Math.min(n,He(e.year(),t));e._isUTC?e._d.setUTCMonth(t,n):e._d.setMonth(t,n)}}function Ie(e){return null!=e?(Ae(this,e),_.updateOffset(this,!0),this):Ce(this,"Month")}function je(){function e(e,t){return t.length-e.length}for(var t,n,s=[],i=[],r=[],a=0;a<12;a++)n=l([2e3,a]),t=f(this.monthsShort(n,"")),n=f(this.months(n,"")),s.push(t),i.push(n),r.push(n),r.push(t);s.sort(e),i.sort(e),r.sort(e),this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Ze(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 qe(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+qe(e,s,i),n=t<=0?We(r=e-1)+t:t>We(e)?(r=e+1,t-We(e)):(r=e,t);return{year:r,dayOfYear:n}}function Be(e,t,n){var s,i,r=qe(e.year(),t,n),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+N(i=e.year()-1,t,n):r>N(e.year(),t,n)?(s=r-N(e.year(),t,n),i=e.year()+1):(i=e.year(),s=r),{week:s,year:i}}function N(e,t,n){var s=qe(e,t,n),t=qe(e+1,t,n);return(We(e)-s+t)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),h("w",n,u),h("ww",n,t),h("W",n,u),h("WW",n,t),Oe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=M(e)});function Je(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"),h("d",n),h("e",n),h("E",n),h("dd",function(e,t){return t.weekdaysMinRegex(e)}),h("ddd",function(e,t){return t.weekdaysShortRegex(e)}),h("dddd",function(e,t){return t.weekdaysRegex(e)}),Oe(["dd","ddd","dddd"],function(e,t,n,s){s=n._locale.weekdaysParse(e,s,n._strict);null!=s?t.d=s:p(n).invalidWeekday=e}),Oe(["d","e","E"],function(e,t,n,s){t[s]=M(e)});var Qe="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Xe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ke="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),et=i,tt=i,nt=i;function st(){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=f(this.weekdaysMin(s,"")),n=f(this.weekdaysShort(s,"")),s=f(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 it(){return this.hours()%12||12}function rt(e,t){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function at(e,t){return t._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,it),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+it.apply(this)+r(this.minutes(),2)}),s("hmmss",0,0,function(){return""+it.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)}),rt("a",!0),rt("A",!1),h("a",at),h("A",at),h("H",n,d),h("h",n,u),h("k",n,u),h("HH",n,t),h("hh",n,t),h("kk",n,t),h("hmm",me),h("hmmss",_e),h("Hmm",me),h("Hmmss",_e),v(["H","HH"],O),v(["k","kk"],function(e,t,n){e=M(e);t[O]=24===e?0:e}),v(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),v(["h","hh"],function(e,t,n){t[O]=M(e),p(n).bigHour=!0}),v("hmm",function(e,t,n){var s=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s)),p(n).bigHour=!0}),v("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s,2)),t[T]=M(e.substr(i)),p(n).bigHour=!0}),v("Hmm",function(e,t,n){var s=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s))}),v("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s,2)),t[T]=M(e.substr(i))});i=Re("Hours",!0);var ot,ut={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:Fe,monthsShort:Le,week:{dow:0,doy:6},weekdays:Qe,weekdaysMin:Ke,weekdaysShort:Xe,meridiemParse:/[ap]\.?m?\.?/i},W={},lt={};function dt(e){return e&&e.toLowerCase().replace("_","-")}function ht(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=dt(e[r]).split("-")).length,n=(n=dt(e[r+1]))?n.split("-"):null;0<t;){if(s=ct(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 ot}function ct(t){var e,n;if(void 0===W[t]&&"undefined"!=typeof module&&module&&module.exports&&(n=t)&&n.match("^[^/\\\\]*$"))try{e=ot._abbr,require("./locale/"+t),ft(e)}catch(e){W[t]=null}return W[t]}function ft(e,t){return e&&((t=g(t)?P(e):mt(e,t))?ot=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),ot._abbr}function mt(e,t){if(null===t)return delete W[e],null;var n,s=ut;if(t.abbr=e,null!=W[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=W[e]._config;else if(null!=t.parentLocale)if(null!=W[t.parentLocale])s=W[t.parentLocale]._config;else{if(null==(n=ct(t.parentLocale)))return lt[t.parentLocale]||(lt[t.parentLocale]=[]),lt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return W[e]=new K(X(s,t)),lt[e]&&lt[e].forEach(function(e){mt(e.name,e.config)}),ft(e),W[e]}function P(e){var t;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return ot;if(!y(e)){if(t=ct(e))return t;e=[e]}return ht(e)}function _t(e){var t=e._a;return t&&-2===p(e).overflow&&(t=t[Y]<0||11<t[Y]?Y:t[S]<1||t[S]>He(t[D],t[Y])?S:t[O]<0||24<t[O]||24===t[O]&&(0!==t[b]||0!==t[T]||0!==t[Te])?O:t[b]<0||59<t[b]?b:t[T]<0||59<t[T]?T:t[Te]<0||999<t[Te]?Te:-1,p(e)._overflowDayOfYear&&(t<D||S<t)&&(t=S),p(e)._overflowWeeks&&-1===t&&(t=xe),p(e)._overflowWeekday&&-1===t&&(t=Ne),p(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/]],Mt=/^\/?Date\((-?\d+)/i,vt=/^(?:(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 Yt(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(p(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||""),xt(e)}else e._isValid=!1}}else e._isValid=!1}function St(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),Le.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=vt.exec(e._i.replace(/\([^()]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));s?(t=St(s[4],s[3],s[2],s[5],s[6],s[7]),function(e,t,n){if(!e||Xe.indexOf(e)===new Date(t[0],t[1],t[2]).getDay())return 1;p(n).weekdayMismatch=!0,n._isValid=!1}(s[1],t,e)&&(e._a=t,e._tzm=(t=s[8],n=s[9],s=s[10],t?Dt[t]:n?0:60*(((t=parseInt(s,10))-(n=t%100))/100)+n),e._d=ze.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),p(e).rfc2822=!0)):e._isValid=!1}function bt(e,t,n){return null!=e?e:null!=t?t:n}function Tt(e){var t,n,s,i,r,a,o,u,l,d,h,c=[];if(!e._d){for(s=e,i=new Date(_.now()),n=s._useUTC?[i.getUTCFullYear(),i.getUTCMonth(),i.getUTCDate()]:[i.getFullYear(),i.getMonth(),i.getDate()],e._w&&null==e._a[S]&&null==e._a[Y]&&(null!=(i=(s=e)._w).GG||null!=i.W||null!=i.E?(u=1,l=4,r=bt(i.GG,s._a[D],Be(R(),1,4).year),a=bt(i.W,1),((o=bt(i.E,1))<1||7<o)&&(d=!0)):(u=s._locale._week.dow,l=s._locale._week.doy,h=Be(R(),u,l),r=bt(i.gg,s._a[D],h.year),a=bt(i.w,h.week),null!=i.d?((o=i.d)<0||6<o)&&(d=!0):null!=i.e?(o=i.e+u,(i.e<0||6<i.e)&&(d=!0)):o=u),a<1||a>N(r,u,l)?p(s)._overflowWeeks=!0:null!=d?p(s)._overflowWeekday=!0:(h=$e(r,a,o,u,l),s._a[D]=h.year,s._dayOfYear=h.dayOfYear)),null!=e._dayOfYear&&(i=bt(e._a[D],n[D]),(e._dayOfYear>We(i)||0===e._dayOfYear)&&(p(e)._overflowDayOfYear=!0),d=ze(i,0,e._dayOfYear),e._a[Y]=d.getUTCMonth(),e._a[S]=d.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[O]&&0===e._a[b]&&0===e._a[T]&&0===e._a[Te]&&(e._nextDay=!0,e._a[O]=0),e._d=(e._useUTC?ze:Ze).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[O]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(p(e).weekdayMismatch=!0)}}function xt(e){if(e._f===_.ISO_8601)Yt(e);else if(e._f===_.RFC_2822)Ot(e);else{e._a=[],p(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)||[],d=l.length,h=0;h<d;h++)n=l[h],(t=(a.match(De(n,e))||[])[0])&&(0<(s=a.substr(0,a.indexOf(t))).length&&p(e).unusedInput.push(s),a=a.slice(a.indexOf(t)+t.length),u+=t.length),ie[n]?(t?p(e).empty=!1:p(e).unusedTokens.push(n),s=n,r=e,null!=(i=t)&&c(Se,s)&&Se[s](i,r._a,r,s)):e._strict&&!t&&p(e).unusedTokens.push(n);p(e).charsLeftOver=o-u,0<a.length&&p(e).unusedInput.push(a),e._a[O]<=12&&!0===p(e).bigHour&&0<e._a[O]&&(p(e).bigHour=void 0),p(e).parsedDateParts=e._a.slice(0),p(e).meridiem=e._meridiem,e._a[O]=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[O],e._meridiem),null!==(o=p(e).era)&&(e._a[D]=e._locale.erasConvertYear(o,e._a[D])),Tt(e),_t(e)}}function Nt(e){var t,n,s,i=e._i,r=e._f;if(e._locale=e._locale||P(e._l),null===i||void 0===r&&""===i)return I({nullInput:!0});if("string"==typeof i&&(e._i=i=e._locale.preparse(i)),k(i))return new $(_t(i));if(V(i))e._d=i;else if(y(r)){var a,o,u,l,d,h,c=e,f=!1,m=c._f.length;if(0===m)p(c).invalidFormat=!0,c._d=new Date(NaN);else{for(l=0;l<m;l++)d=0,h=!1,a=q({},c),null!=c._useUTC&&(a._useUTC=c._useUTC),a._f=c._f[l],xt(a),A(a)&&(h=!0),d=(d+=p(a).charsLeftOver)+10*p(a).unusedTokens.length,p(a).score=d,f?d<u&&(u=d,o=a):(null==u||d<u||h)&&(u=d,o=a,h)&&(f=!0);E(c,o||a)}}else if(r)xt(e);else if(g(r=(i=e)._i))i._d=new Date(_.now());else V(r)?i._d=new Date(r.valueOf()):"string"==typeof r?(n=i,null!==(t=Mt.exec(n._i))?n._d=new Date(+t[1]):(Yt(n),!1===n._isValid&&(delete n._isValid,Ot(n),!1===n._isValid)&&(delete n._isValid,n._strict?n._isValid=!1:_.createFromInputFallback(n)))):y(r)?(i._a=G(r.slice(0),function(e){return parseInt(e,10)}),Tt(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)}),Tt(t)):w(r)?i._d=new Date(r):_.createFromInputFallback(i);return A(e)||(e._d=null),e}function Wt(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)||y(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 $(_t(Nt(i=r))))._nextDay&&(i.add(1,"d"),i._nextDay=void 0),i}function R(e,t,n,s){return Wt(e,t,n,s,!1)}_.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":""))}),_.ISO_8601=function(){},_.RFC_2822=function(){};me=e("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=R.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:I()}),_e=e("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=R.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:I()});function Pt(e,t){var n,s;if(!(t=1===t.length&&y(t[0])?t[0]:t).length)return R();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Rt=["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=Rt.length;for(t in e)if(c(e,t)&&(-1===x.call(Rt,t)||null!=e[t]&&isNaN(e[t])))return!1;for(n=0;n<i;++n)if(e[Rt[n]]){if(s)return!1;parseFloat(e[Rt[n]])!==M(e[Rt[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=P(),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",""),h("Z",ve),h("ZZ",ve),v(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Vt(ve,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]+M(e[2]))?0:"+"===e[0]?t:-t}function Gt(e,t){var n;return t._isUTC?(t=t.clone(),n=(k(e)||V(e)?e:R(e)).valueOf()-t.valueOf(),t._d.setTime(t._d.valueOf()+n),_.updateOffset(t,!1),t):R(e).local()}function Et(e){return-Math.round(e._d.getTimezoneOffset())}function At(){return!!this.isValid()&&this._isUTC&&0===this._offset}_.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;return Ut(e)?s={ms:e._milliseconds,d:e._days,M:e._months}:w(e)||!isNaN(+e)?(s={},t?s[t]=+e:s.milliseconds=+e):(t=It.exec(e))?(n="-"===t[1]?-1:1,s={y:0,d:M(t[S])*n,h:M(t[O])*n,m:M(t[b])*n,s:M(t[T])*n,ms:M(Ht(1e3*t[Te]))*n}):(t=jt.exec(e))?(n="-"===t[1]?-1:1,s={y:Zt(t[2],n),M:Zt(t[3],n),w:Zt(t[4],n),d:Zt(t[5],n),h:Zt(t[6],n),m:Zt(t[7],n),s:Zt(t[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}(R(s.from),R(s.to)),(s={}).ms=t.milliseconds,s.M=t.months),n=new Ct(s),Ut(e)&&c(e,"_locale")&&(n._locale=e._locale),Ut(e)&&c(e,"_isValid")&&(n._isValid=e._isValid),n}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 qt(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),$t(this,C(e,t),s),this}}function $t(e,t,n,s){var i=t._milliseconds,r=Ht(t._days),t=Ht(t._months);e.isValid()&&(s=null==s||s,t&&Ae(e,Ce(e,"Month")+t*n),r&&Ue(e,"Date",Ce(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s)&&_.updateOffset(e,r||t)}C.fn=Ct.prototype,C.invalid=function(){return C(NaN)};Fe=qt(1,"add"),Qe=qt(-1,"subtract");function Bt(e){return"string"==typeof e||e instanceof String}function Jt(e){return k(e)||V(e)||Bt(e)||w(e)||function(t){var e=y(t),n=!1;e&&(n=0===t.filter(function(e){return!w(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){var n,s;return e.date()<t.date()?-Qt(t,e):-((n=12*(t.year()-e.year())+(t.month()-e.month()))+(t-(s=e.clone().add(n,"months"))<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(1+n,"months")-s)))||0}function Xt(e){return void 0===e?this._locale._abbr:(null!=(e=P(e))&&(this._locale=e),this)}_.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",_.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";Ke=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=[],r=[],a=[],o=this.eras(),u=0,l=o.length;u<l;++u)e=f(o[u].name),t=f(o[u].abbr),n=f(o[u].narrow),i.push(e),s.push(t),r.push(n),a.push(e),a.push(t),a.push(n);this._erasRegex=new RegExp("^("+a.join("|")+")","i"),this._erasNameRegex=new RegExp("^("+i.join("|")+")","i"),this._erasAbbrRegex=new RegExp("^("+s.join("|")+")","i"),this._erasNarrowRegex=new RegExp("^("+r.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?Be(this,s,i).year:(r=N(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"),h("N",rn),h("NN",rn),h("NNN",rn),h("NNNN",function(e,t){return t.erasNameRegex(e)}),h("NNNNN",function(e,t){return t.erasNarrowRegex(e)}),v(["N","NN","NNN","NNNN","NNNNN"],function(e,t,n,s){s=n._locale.erasParse(e,s,n._strict);s?p(n).era=s:p(n).invalidEra=e}),h("y",pe),h("yy",pe),h("yyy",pe),h("yyyy",pe),h("yo",function(e,t){return t._eraYearOrdinalRegex||pe}),v(["y","yy","yyy","yyyy"],D),v(["yo"],function(e,t,n,s){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[D]=n._locale.eraYearOrdinalParse(e,i):t[D]=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"),h("G",ke),h("g",ke),h("GG",n,t),h("gg",n,t),h("GGGG",ge,ce),h("gggg",ge,ce),h("GGGGG",we,fe),h("ggggg",we,fe),Oe(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=M(e)}),Oe(["gg","GG"],function(e,t,n,s){t[s]=_.parseTwoDigitYear(e)}),s("Q",0,"Qo","quarter"),h("Q",de),v("Q",function(e,t){t[Y]=3*(M(e)-1)}),s("D",["DD",2],"Do","date"),h("D",n,u),h("DD",n,t),h("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),v(["D","DD"],S),v("Do",function(e,t){t[S]=M(e.match(n)[0])});ge=Re("Date",!0);s("DDD",["DDDD",3],"DDDo","dayOfYear"),h("DDD",ye),h("DDDD",he),v(["DDD","DDDD"],function(e,t,n){n._dayOfYear=M(e)}),s("m",["mm",2],0,"minute"),h("m",n,d),h("mm",n,t),v(["m","mm"],b);var ln,ce=Re("Minutes",!1),we=(s("s",["ss",2],0,"second"),h("s",n,d),h("ss",n,t),v(["s","ss"],T),Re("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()}),h("S",ye,de),h("SS",ye,t),h("SSS",ye,he),ln="SSSS";ln.length<=9;ln+="S")h(ln,pe);function dn(e,t){t[Te]=M(1e3*("0."+e))}for(ln="S";ln.length<=9;ln+="S")v(ln,dn);fe=Re("Milliseconds",!1),s("z",0,0,"zoneAbbr"),s("zz",0,0,"zoneName");u=$.prototype;function hn(e){return e}u.add=Fe,u.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||R(),n=Gt(e,this).startOf("day"),n=_.calendarFormat(this,n)||"sameElse",t=t&&(a(t[n])?t[n].call(this,e):t[n]);return this.format(t||this.localeData().calendar(n,this,R(e)))},u.clone=function(){return new $(this)},u.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=o(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:m(r)},u.endOf=function(e){var t,n;if(void 0!==(e=o(e))&&"millisecond"!==e&&this.isValid()){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}this._d.setTime(t),_.updateOffset(this,!0)}return this},u.format=function(e){return e=e||(this.isUtc()?_.defaultFormatUtc:_.defaultFormat),e=re(this,e),this.localeData().postformat(e)},u.from=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||R(e).isValid())?C({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},u.fromNow=function(e){return this.from(R(),e)},u.to=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||R(e).isValid())?C({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},u.toNow=function(e){return this.to(R(),e)},u.get=function(e){return a(this[e=o(e)])?this[e]():this},u.invalidAt=function(){return p(this).overflow},u.isAfter=function(e,t){return e=k(e)?e:R(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=o(t)||"millisecond")?this.valueOf()>e.valueOf():e.valueOf()<this.clone().startOf(t).valueOf())},u.isBefore=function(e,t){return e=k(e)?e:R(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=o(t)||"millisecond")?this.valueOf()<e.valueOf():this.clone().endOf(t).valueOf()<e.valueOf())},u.isBetween=function(e,t,n,s){return e=k(e)?e:R(e),t=k(t)?t:R(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))},u.isSame=function(e,t){var e=k(e)?e:R(e);return!(!this.isValid()||!e.isValid())&&("millisecond"===(t=o(t)||"millisecond")?this.valueOf()===e.valueOf():(e=e.valueOf(),this.clone().startOf(t).valueOf()<=e&&e<=this.clone().endOf(t).valueOf()))},u.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},u.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},u.isValid=function(){return A(this)},u.lang=Ke,u.locale=Xt,u.localeData=Kt,u.max=_e,u.min=me,u.parsingFlags=function(){return E({},p(this))},u.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(a(this[e=o(e)]))return this[e](t);return this},u.startOf=function(e){var t,n;if(void 0!==(e=o(e))&&"millisecond"!==e&&this.isValid()){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}this._d.setTime(t),_.updateOffset(this,!0)}return this},u.subtract=Qe,u.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},u.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()}},u.toDate=function(){return new Date(this.valueOf())},u.toISOString=function(e){var t;return this.isValid()?(t=(e=!0!==e)?this.clone().utc():this).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"):a(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"):null},u.inspect=function(){var e,t,n;return this.isValid()?(t="moment",e="",this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z"),t="["+t+'("]',n=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",this.format(t+n+"-MM-DD[T]HH:mm:ss.SSS"+(e+'[")]'))):"moment.invalid(/* "+this._i+" */)"},"undefined"!=typeof Symbol&&null!=Symbol.for&&(u[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),u.toJSON=function(){return this.isValid()?this.toISOString():null},u.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},u.unix=function(){return Math.floor(this.valueOf()/1e3)},u.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},u.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},u.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""},u.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""},u.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""},u.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()-_(n[s].since).year())*e+n[s].offset;return this.year()},u.year=Pe,u.isLeapYear=function(){return be(this.year())},u.weekYear=function(e){return un.call(this,e,this.week(),this.weekday()+this.localeData()._week.dow,this.localeData()._week.dow,this.localeData()._week.doy)},u.isoWeekYear=function(e){return un.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},u.quarter=u.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},u.month=Ie,u.daysInMonth=function(){return He(this.year(),this.month())},u.week=u.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},u.isoWeek=u.isoWeeks=function(e){var t=Be(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},u.weeksInYear=function(){var e=this.localeData()._week;return N(this.year(),e.dow,e.doy)},u.weeksInWeekYear=function(){var e=this.localeData()._week;return N(this.weekYear(),e.dow,e.doy)},u.isoWeeksInYear=function(){return N(this.year(),1,4)},u.isoWeeksInISOWeekYear=function(){return N(this.isoWeekYear(),1,4)},u.date=ge,u.day=u.days=function(e){var t,n,s;return this.isValid()?(t=Ce(this,"Day"),null!=e?(n=e,s=this.localeData(),e="string"!=typeof n?n:isNaN(n)?"number"==typeof(n=s.weekdaysParse(n))?n:null:parseInt(n,10),this.add(e-t,"d")):t):null!=e?this:NaN},u.weekday=function(e){var t;return this.isValid()?(t=(this.day()+7-this.localeData()._week.dow)%7,null==e?t:this.add(e-t,"d")):null!=e?this:NaN},u.isoWeekday=function(e){var t,n;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},u.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")},u.hour=u.hours=i,u.minute=u.minutes=ce,u.second=u.seconds=we,u.millisecond=u.milliseconds=fe,u.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(ve,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?$t(this,C(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,_.updateOffset(this,!0),this._changeInProgress=null)),this},u.utc=function(e){return this.utcOffset(0,e)},u.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e)&&this.subtract(Et(this),"m"),this},u.parseZone=function(){var e;return null!=this._tzm?this.utcOffset(this._tzm,!1,!0):"string"==typeof this._i&&(null!=(e=Vt(Me,this._i))?this.utcOffset(e):this.utcOffset(0,!0)),this},u.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?R(e).utcOffset():0,(this.utcOffset()-e)%60==0)},u.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},u.isLocal=function(){return!!this.isValid()&&!this._isUTC},u.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},u.isUtc=At,u.isUTC=At,u.zoneAbbr=function(){return this._isUTC?"UTC":""},u.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},u.dates=e("dates accessor is deprecated. Use date instead.",ge),u.months=e("months accessor is deprecated. Use month instead",Ie),u.years=e("years accessor is deprecated. Use year instead",Pe),u.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()}),u.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){var e,t;return g(this._isDSTShifted)&&(q(e={},this),(e=Nt(e))._a?(t=(e._isUTC?l:R)(e._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&&M(e[a])!==M(t[a]))&&r++;return r+i}(e._a,t.toArray())):this._isDSTShifted=!1),this._isDSTShifted});d=K.prototype;function cn(e,t,n,s){var i=P(),s=l().set(s,t);return i[n](s,e)}function fn(e,t,n){if(w(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?w(t)&&(n=t,t=void 0):(t=e,e=!1,w(n=t)&&(n=t,t=void 0)),t||"");var i,r=P(),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}d.calendar=function(e,t,n){return a(e=this._calendar[e]||this._calendar.sameElse)?e.call(t,n):e},d.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])},d.invalidDate=function(){return this._invalidDate},d.ordinal=function(e){return this._ordinal.replace("%d",e)},d.preparse=hn,d.postformat=hn,d.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return a(i)?i(e,t,n,s):i.replace(/%d/i,e)},d.pastFuture=function(e,t){return a(e=this._relativeTime[0<e?"future":"past"])?e(t):e.replace(/%s/i,t)},d.set=function(e){var t,n;for(n in e)c(e,n)&&(a(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)},d.eras=function(e,t){for(var n,s=this._eras||P("en")._eras,i=0,r=s.length;i<r;++i){switch(typeof s[i].since){case"string":n=_(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=_(s[i].until).startOf("day").valueOf(),s[i].until=n.valueOf();break}}return s},d.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]},d.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?_(e.since).year():_(e.since).year()+(t-e.offset)*n},d.erasAbbrRegex=function(e){return c(this,"_erasAbbrRegex")||an.call(this),e?this._erasAbbrRegex:this._erasRegex},d.erasNameRegex=function(e){return c(this,"_erasNameRegex")||an.call(this),e?this._erasNameRegex:this._erasRegex},d.erasNarrowRegex=function(e){return c(this,"_erasNarrowRegex")||an.call(this),e?this._erasNarrowRegex:this._erasRegex},d.months=function(e,t){return e?(y(this._months)?this._months:this._months[(this._months.isFormat||Ve).test(t)?"format":"standalone"])[e.month()]:y(this._months)?this._months:this._months.standalone},d.monthsShort=function(e,t){return e?(y(this._monthsShort)?this._monthsShort:this._monthsShort[Ve.test(t)?"format":"standalone"])[e.month()]:y(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},d.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=x.call(this._shortMonthsParse,e))?i:null:-1!==(i=x.call(this._longMonthsParse,e))?i:null:"MMM"===t?-1!==(i=x.call(this._shortMonthsParse,e))||-1!==(i=x.call(this._longMonthsParse,e))?i:null:-1!==(i=x.call(this._longMonthsParse,e))||-1!==(i=x.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}},d.monthsRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||je.call(this),e?this._monthsStrictRegex:this._monthsRegex):(c(this,"_monthsRegex")||(this._monthsRegex=Ee),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},d.monthsShortRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||je.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(c(this,"_monthsShortRegex")||(this._monthsShortRegex=Ge),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},d.week=function(e){return Be(e,this._week.dow,this._week.doy).week},d.firstDayOfYear=function(){return this._week.doy},d.firstDayOfWeek=function(){return this._week.dow},d.weekdays=function(e,t){return t=y(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"],!0===e?Je(t,this._week.dow):e?t[e.day()]:t},d.weekdaysMin=function(e){return!0===e?Je(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},d.weekdaysShort=function(e){return!0===e?Je(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},d.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=x.call(this._weekdaysParse,e))?i:null:"ddd"===t?-1!==(i=x.call(this._shortWeekdaysParse,e))?i:null:-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:"dddd"===t?-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.call(this._shortWeekdaysParse,e))||-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:"ddd"===t?-1!==(i=x.call(this._shortWeekdaysParse,e))||-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:-1!==(i=x.call(this._minWeekdaysParse,e))||-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.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}},d.weekdaysRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(c(this,"_weekdaysRegex")||(this._weekdaysRegex=et),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},d.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(c(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=tt),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},d.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(c(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=nt),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},d.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},d.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ft("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===M(e%100/10)?"th":1==t?"st":2==t?"nd":3==t?"rd":"th")}}),_.lang=e("moment.lang is deprecated. Use moment.locale instead.",ft),_.langData=e("moment.langData is deprecated. Use moment.localeData instead.",P);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)}}de=kn("ms"),t=kn("s"),ye=kn("m"),he=kn("h"),Fe=kn("d"),_e=kn("w"),me=kn("M"),Qe=kn("Q"),i=kn("y"),ce=de;function Mn(e){return function(){return this.isValid()?this._data[e]:NaN}}var we=Mn("milliseconds"),fe=Mn("seconds"),ge=Mn("minutes"),Pe=Mn("hours"),d=Mn("days"),vn=Mn("months"),Dn=Mn("years");var Yn=Math.round,Sn={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function On(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")),d=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||(d<=1?["w"]:d<n.w&&["ww",d]):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 bn=Math.abs;function Tn(e){return(0<e)-(e<0)||+e}function xn(){var e,t,n,s,i,r,a,o,u,l,d;return this.isValid()?(e=bn(this._milliseconds)/1e3,t=bn(this._days),n=bn(this._months),(o=this.asSeconds())?(s=m(e/60),i=m(s/60),e%=60,s%=60,r=m(n/12),n%=12,a=e?e.toFixed(3).replace(/\.?0+$/,""):"",u=Tn(this._months)!==Tn(o)?"-":"",l=Tn(this._days)!==Tn(o)?"-":"",d=Tn(this._milliseconds)!==Tn(o)?"-":"",(o<0?"-":"")+"P"+(r?u+r+"Y":"")+(n?u+n+"M":"")+(t?l+t+"D":"")+(i||s||e?"T":"")+(i?d+i+"H":"")+(s?d+s+"M":"")+(e?d+a+"S":"")):"P0D"):this.localeData().invalidDate()}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=o(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=de,U.asSeconds=t,U.asMinutes=ye,U.asHours=he,U.asDays=Fe,U.asWeeks=_e,U.asMonths=me,U.asQuarters=Qe,U.asYears=i,U.valueOf=ce,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=m(e/1e3),s.seconds=e%60,e=m(e/60),s.minutes=e%60,e=m(e/60),s.hours=e%24,t+=m(e/24),n+=e=m(wn(t)),t-=gn(pn(e)),e=m(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=o(e),this.isValid()?this[e+"s"]():NaN},U.milliseconds=we,U.seconds=fe,U.minutes=ge,U.hours=Pe,U.days=d,U.weeks=function(){return m(this.days()/7)},U.months=vn,U.years=Dn,U.humanize=function(e,t){var n,s;return this.isValid()?(n=!1,s=Sn,"object"==typeof e&&(t=e,e=!1),"boolean"==typeof e&&(n=e),"object"==typeof t&&(s=Object.assign({},Sn,t),null!=t.s)&&null==t.ss&&(s.ss=t.s-1),e=this.localeData(),t=On(this,!n,s,e),n&&(t=e.pastFuture(+this,t)),e.postformat(t)):this.localeData().invalidDate()},U.toISOString=xn,U.toString=xn,U.toJSON=xn,U.locale=Xt,U.localeData=Kt,U.toIsoString=e("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",xn),U.lang=Ke,s("X",0,0,"unix"),s("x",0,0,"valueOf"),h("x",ke),h("X",/[+-]?\d+(\.\d{1,3})?/),v("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e))}),v("x",function(e,t,n){n._d=new Date(M(e))}),_.version="2.30.1",H=R,_.fn=u,_.min=function(){return Pt("isBefore",[].slice.call(arguments,0))},_.max=function(){return Pt("isAfter",[].slice.call(arguments,0))},_.now=function(){return Date.now?Date.now():+new Date},_.utc=l,_.unix=function(e){return R(1e3*e)},_.months=function(e,t){return fn(e,t,"months")},_.isDate=V,_.locale=ft,_.invalid=I,_.duration=C,_.isMoment=k,_.weekdays=function(e,t,n){return mn(e,t,n,"weekdays")},_.parseZone=function(){return R.apply(null,arguments).parseZone()},_.localeData=P,_.isDuration=Ut,_.monthsShort=function(e,t){return fn(e,t,"monthsShort")},_.weekdaysMin=function(e,t,n){return mn(e,t,n,"weekdaysMin")},_.defineLocale=mt,_.updateLocale=function(e,t){var n,s;return null!=t?(s=ut,null!=W[e]&&null!=W[e].parentLocale?W[e].set(X(W[e]._config,t)):(t=X(s=null!=(n=ct(e))?n._config:s,t),null==n&&(t.abbr=e),(s=new K(t)).parentLocale=W[e],W[e]=s),ft(e)):null!=W[e]&&(null!=W[e].parentLocale?(W[e]=W[e].parentLocale,e===ft()&&ft(e)):null!=W[e]&&delete W[e]),W[e]},_.locales=function(){return ee(W)},_.weekdaysShort=function(e,t,n){return mn(e,t,n,"weekdaysShort")},_.normalizeUnits=o,_.relativeTimeRounding=function(e){return void 0===e?Yn:"function"==typeof e&&(Yn=e,!0)},_.relativeTimeThreshold=function(e,t){return void 0!==Sn[e]&&(void 0===t?Sn[e]:(Sn[e]=t,"s"===e&&(Sn.ss=t-1),!0))},_.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"},_.prototype=u,_.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"},_});
vendor: 4,990 bytes, line 11782
11782!function(a,i){"use strict";"object"==typeof module&&module.exports?module.exports=i(require("moment")):"function"==typeof define&&define.amd?define(["moment"],i):i(a.moment)}(this,function(o){"use strict";void 0===o.version&&o.default&&(o=o.default);var i,s={},c={},A={},u={},m={},a=(o&&"string"==typeof o.version||y("Moment Timezone requires Moment.js. See https://momentjs.com/timezone/docs/#/use-it/browser/"),o.version.split(".")),r=+a[0],e=+a[1];function n(a){return 96<a?a-87:64<a?a-29:a-48}function t(a){var i=0,r=a.split("."),e=r[0],o=r[1]||"",c=1,A=0,r=1;for(45===a.charCodeAt(0)&&(r=-(i=1));i<e.length;i++)A=60*A+n(e.charCodeAt(i));for(i=0;i<o.length;i++)c/=60,A+=n(o.charCodeAt(i))*c;return A*r}function l(a){for(var i=0;i<a.length;i++)a[i]=t(a[i])}function f(a,i){for(var r=[],e=0;e<i.length;e++)r[e]=a[i[e]];return r}function p(a){for(var a=a.split("|"),i=a[2].split(" "),r=a[3].split(""),e=a[4].split(" "),o=(l(i),l(r),l(e),e),c=r.length,A=0;A<c;A++)o[A]=Math.round((o[A-1]||0)+6e4*o[A]);return o[c-1]=1/0,{name:a[0],abbrs:f(a[1].split(" "),r),offsets:f(i,r),untils:e,population:0|a[5]}}function M(a){a&&this._set(p(a))}function b(a,i){this.name=a,this.zones=i}function h(a){var i=a.toTimeString(),r=i.match(/\([a-z ]+\)/i);"GMT"===(r=r&&r[0]?(r=r[0].match(/[A-Z]/g))?r.join(""):void 0:(r=i.match(/[A-Z]{3,5}/g))?r[0]:void 0)&&(r=void 0),this.at=+a,this.abbr=r,this.offset=a.getTimezoneOffset()}function d(a){this.zone=a,this.offsetScore=0,this.abbrScore=0}function E(){for(var a,i,r,e=(new Date).getFullYear()-2,o=new h(new Date(e,0,1)),c=o.offset,A=[o],n=1;n<48;n++)(r=new Date(e,n,1).getTimezoneOffset())!==c&&(a=function(a,i){for(var r;r=6e4*((i.at-a.at)/12e4|0);)(r=new h(new Date(a.at+r))).offset===a.offset?a=r:i=r;return a}(o,i=new h(new Date(e,n,1))),A.push(a),A.push(new h(new Date(a.at+6e4))),o=i,c=r);for(n=0;n<4;n++)A.push(new h(new Date(e+n,0,1))),A.push(new h(new Date(e+n,6,1)));return A}function g(a,i){return a.offsetScore!==i.offsetScore?a.offsetScore-i.offsetScore:a.abbrScore!==i.abbrScore?a.abbrScore-i.abbrScore:a.zone.population!==i.zone.population?i.zone.population-a.zone.population:i.zone.name.localeCompare(a.zone.name)}function P(){try{var a=Intl.DateTimeFormat().resolvedOptions().timeZone;if(a&&3<a.length){var i=u[S(a)];if(i)return i;y("Moment Timezone found "+a+" from the Intl api, but did not have that data loaded.")}}catch(a){}for(var r,e,o=E(),c=o.length,A=function(a){for(var i,r,e,o=a.length,c={},A=[],n={},t=0;t<o;t++)if(r=a[t].offset,!n.hasOwnProperty(r)){for(i in e=m[r]||{})e.hasOwnProperty(i)&&(c[i]=!0);n[r]=!0}for(t in c)c.hasOwnProperty(t)&&A.push(u[t]);return A}(o),n=[],t=0;t<A.length;t++){for(r=new d(z(A[t])),e=0;e<c;e++)r.scoreOffsetAt(o[e]);n.push(r)}return n.sort(g),0<n.length?n[0].zone.name:void 0}function S(a){return(a||"").toLowerCase().replace(/\//g,"_")}function T(a){var i,r,e,o;for("string"==typeof a&&(a=[a]),i=0;i<a.length;i++){o=S(r=(e=a[i].split("|"))[0]),s[o]=a[i],u[o]=r,A=c=t=n=void 0;var c,A,n=o,t=e[2].split(" ");for(l(t),c=0;c<t.length;c++)A=t[c],m[A]=m[A]||{},m[A][n]=!0}}function z(a,i){a=S(a);var r=s[a];return r instanceof M?r:"string"==typeof r?(r=new M(r),s[a]=r):c[a]&&i!==z&&(i=z(c[a],z))?((r=s[a]=new M)._set(i),r.name=u[a],r):null}function k(a){var i,r,e,o;for("string"==typeof a&&(a=[a]),i=0;i<a.length;i++)e=S((r=a[i].split("|"))[0]),o=S(r[1]),c[e]=o,u[e]=r[0],c[o]=e,u[o]=r[1]}function _(a){T(a.zones),k(a.links);var i,r,e,o=a.countries;if(o&&o.length)for(i=0;i<o.length;i++)r=(e=o[i].split("|"))[0].toUpperCase(),e=e[1].split(" "),A[r]=new b(r,e);L.dataVersion=a.version}function C(a){return C.didShowError||(C.didShowError=!0,y("moment.tz.zoneExists('"+a+"') has been deprecated in favor of !moment.tz.zone('"+a+"')")),!!z(a)}function B(a){var i="X"===a._f||"x"===a._f;return!(!a._a||void 0!==a._tzm||i)}function y(a){"undefined"!=typeof console&&"function"==typeof console.error&&console.error(a)}function L(a){var i=Array.prototype.slice.call(arguments,0,-1),r=arguments[arguments.length-1],i=o.utc.apply(null,i);return!o.isMoment(a)&&B(i)&&(a=z(r))&&i.add(a.parse(i),"minutes"),i.tz(r),i}(r<2||2==r&&e<6)&&y("Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js "+o.version+". See momentjs.com"),M.prototype={_set:function(a){this.name=a.name,this.abbrs=a.abbrs,this.untils=a.untils,this.offsets=a.offsets,this.population=a.population},_index:function(a){a=function(a,i){var r,e=i.length;if(a<i[0])return 0;if(1<e&&i[e-1]===1/0&&a>=i[e-2])return e-1;if(a>=i[e-1])return-1;for(var o=0,c=e-1;1<c-o;)i[r=Math.floor((o+c)/2)]<=a?o=r:c=r;return c}(+a,this.untils);if(0<=a)return a},countries:function(){var i=this.name;return Object.keys(A).filter(function(a){return-1!==A[a].zones.indexOf(i)})},parse:function(a){for(var i,r,e,o=+a,c=this.offsets,A=this.untils,n=A.length-1,t=0;t<n;t++)if(i=c[t],r=c[t+1],e=c[t&&t-1],i<r&&L.moveAmbiguousForward?i=r:e<i&&L.moveInvalidForward&&(i=e),o<A[t]-6e4*i)return c[t];return c[n]},abbr:function(a){return this.abbrs[this._index(a)]}
vendor: 4,616 bytes, line 11782
11782,offset:function(a){return y("zone.offset has been deprecated in favor of zone.utcOffset"),this.offsets[this._index(a)]},utcOffset:function(a){return this.offsets[this._index(a)]}},d.prototype.scoreOffsetAt=function(a){this.offsetScore+=Math.abs(this.zone.utcOffset(a.at)-a.offset),this.zone.abbr(a.at).replace(/[^A-Z]/g,"")!==a.abbr&&this.abbrScore++},L.version="0.5.45",L.dataVersion="",L._zones=s,L._links=c,L._names=u,L._countries=A,L.add=T,L.link=k,L.load=_,L.zone=z,L.zoneExists=C,L.guess=function(a){return i=i&&!a?i:P()},L.names=function(){var a,i=[];for(a in u)u.hasOwnProperty(a)&&(s[a]||s[c[a]])&&u[a]&&i.push(u[a]);return i.sort()},L.Zone=M,L.unpack=p,L.unpackBase60=t,L.needsOffset=B,L.moveInvalidForward=!0,L.moveAmbiguousForward=!1,L.countries=function(){return Object.keys(A)},L.zonesForCountry=function(a,i){var r;return r=(r=a).toUpperCase(),(a=A[r]||null)?(r=a.zones.sort(),i?r.map(function(a){return{name:a,offset:z(a).utcOffset(new Date)}}):r):null};var D,a=o.fn;function N(a){return function(){return this._z?this._z.abbr(this):a.call(this)}}function O(a){return function(){return this._z=null,a.apply(this,arguments)}}o.tz=L,o.defaultZone=null,o.updateOffset=function(a,i){var r,e=o.defaultZone;void 0===a._z&&(e&&B(a)&&!a._isUTC&&a.isValid()&&(a._d=o.utc(a._a)._d,a.utc().add(e.parse(a),"minutes")),a._z=e),a._z&&(e=a._z.utcOffset(a),Math.abs(e)<16&&(e/=60),void 0!==a.utcOffset?(r=a._z,a.utcOffset(-e,i),a._z=r):a.zone(e,i))},a.tz=function(a,i){if(a){if("string"!=typeof a)throw new Error("Time zone name must be a string, got "+a+" ["+typeof a+"]");return this._z=z(a),this._z?o.updateOffset(this,i):y("Moment Timezone has no data for "+a+". See http://momentjs.com/timezone/docs/#/data-loading/."),this}if(this._z)return this._z.name},a.zoneName=N(a.zoneName),a.zoneAbbr=N(a.zoneAbbr),a.utc=O(a.utc),a.local=O(a.local),a.utcOffset=(D=a.utcOffset,function(){return 0<arguments.length&&(this._z=null),D.apply(this,arguments)}),o.tz.setDefault=function(a){return(r<2||2==r&&e<9)&&y("Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js "+o.version+"."),o.defaultZone=a?z(a):null,o};a=o.momentProperties;return"[object Array]"===Object.prototype.toString.call(a)?(a.push("_z"),a.push("_a")):a&&(a._z=null),_({version:"2024a",zones:["Africa/Abidjan|GMT|0|0||48e5","Africa/Nairobi|EAT|-30|0||47e5","Africa/Algiers|CET|-10|0||26e5","Africa/Lagos|WAT|-10|0||17e6","Africa/Khartoum|CAT|-20|0||51e5","Africa/Cairo|EET EEST|-20 -30|010101010101010|29NW0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0|15e6","Africa/Casablanca|+01 +00|-10 0|010101010101010101010101|208q0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600|32e5","Europe/Paris|CET CEST|-10 -20|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|11e6","Africa/Johannesburg|SAST|-20|0||84e5","Africa/Juba|EAT CAT|-30 -20|01|24nx0|","Africa/Sao_Tome|WAT GMT|-10 0|01|1XiN0|","Africa/Tripoli|EET|-20|0||11e5","America/Adak|HST HDT|a0 90|01010101010101010101010|1XKc0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|326","America/Anchorage|AKST AKDT|90 80|01010101010101010101010|1XKb0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|30e4","America/Santo_Domingo|AST|40|0||29e5","America/Fortaleza|-03|30|0||34e5","America/Asuncion|-03 -04|30 40|01010101010101010101010|1XPD0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0|28e5","America/Panama|EST|50|0||15e5","America/Mexico_City|CST CDT|60 50|010101010|1XVk0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|20e6","America/Managua|CST|60|0||22e5","America/Caracas|-04|40|0||29e5","America/Lima|-05|50|0||11e6","America/Denver|MST MDT|70 60|01010101010101010101010|1XK90 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|26e5","America/Campo_Grande|-03 -04|30 40|01|1XBD0|77e4","America/Chicago|CST CDT|60 50|01010101010101010101010|1XK80 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|92e5","America/Chihuahua|MST MDT CST|70 60 60|010101012|1XVl0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|81e4","America/Ciudad_Juarez|MST MDT CST|70 60 60|010101012010101010101010|1XK90 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 cm0 EP0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|","America/Phoenix|MST|70|0||42e5","America/Whitehorse|PST PDT MST|80 70 70|01012|1XKa0 1zb0 Op0 1z90|23e3","America/Ne
11782w_York|EST EDT|50 40|01010101010101010101010|1XK70 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|21e6","America/Los_Angeles|PST PDT|80 70|01010101010101010101010|1XKa0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|15e6","America/Halifax|AST ADT|40 30|01010101010101010101010|1XK60 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|39e4","America/Godthab|-03 -02 -01|30 20 10|0101010101212121212121|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 2so0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|17e3","America/Havana|CST CDT|50 40|01010101010101010101010|1XK50 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0|21e5","America/Mazatlan|MST MDT|70 60|010101010|1XVl0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|44e4","America/Metlakatla|PST AKST AKDT|80 90 80|012121212121212121212121|1Xqy0 jB0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|14e2","America/Miquelon|-03 -02|30 20|01010101010101010101010|1XK50 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|61e2","America/Noronha|-02|20|0||30e2","America/Ojinaga|MST MDT CST CDT|70 60 60 50|01010101232323232323232|1XK90 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 Rc0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|23e3","America/Santiago|-03 -04|30 40|01010101010101010101010|1XVf0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0|62e5","America/Sao_Paulo|-02 -03|20 30|01|1XBC0|20e6","America/Scoresbysund|-01 +00 -02|10 0 20|0101010101020202020202|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 2pA0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|452","America/St_Johns|NST NDT|3u 2u|01010101010101010101010|1XK5u 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|11e4","Antarctica/Casey|+11 +08|-b0 -80|0101010101|1XME0 1kr0 12l0 1o01 14kX 1lf1 14kX 1lf1 13bX|10","Asia/Bangkok|+07|-70|0||15e6","Asia/Vladivostok|+10|-a0|0||60e4","Australia/Sydney|AEDT AEST|-b0 -a0|01010101010101010101010|1XV40 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0|40e5","Asia/Tashkent|+05|-50|0||23e5","Pacific/Auckland|NZDT NZST|-d0 -c0|01010101010101010101010|1XV20 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0|14e5","Europe/Istanbul|+03|-30|0||13e6","Antarctica/Troll|+00 +02|0 -20|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|40","Antarctica/Vostok|+07 +05|-70 -50|01|2bnv0|25","Asia/Almaty|+06 +05|-60 -50|01|2bR60|15e5","Asia/Amman|EET EEST +03|-20 -30 -30|010101012|1XRy0 1o00 11A0 1qM0 WM0 1qM0 LA0 1C00|25e5","Asia/Kamchatka|+12|-c0|0||18e4","Asia/Dubai|+04|-40|0||39e5","Asia/Beirut|EET EEST|-20 -30|01010101010101010101010|1XSm0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|22e5","Asia/Dhaka|+06|-60|0||16e6","Asia/Kuala_Lumpur|+08|-80|0||71e5","Asia/Kolkata|IST|-5u|0||15e6","Asia/Chita|+09|-90|0||33e4","Asia/Shanghai|CST|-80|0||23e6","Asia/Colombo|+0530|-5u|0||22e5","Asia/Damascus|EET EEST +03|-20 -30 -30|010101012|1XRy0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|26e5","Europe/Athens|EET EEST|-20 -30|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|35e5","Asia/Gaza|EET EEST|-20 -30|01010101010101010101010|1XRy0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 1cN0 1cL0 1a10 1fz0 17d0 1in0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|18e5","Asia/Hong_Kong|HKT|-80|0||73e5","Asia/Jakarta|WIB|-70|0||31e6","Asia/Jayapura|WIT|-90|0||26e4","Asia/Jerusalem|IST IDT|-20 -30|01010101010101010101010|1XRA0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0|81e4","Asia/Kabul|+0430|-4u|0||46e5","Asia/Karachi|PKT|-50|0||24e6","Asia/Kathmandu|+0545|-5J|0||12e5","Asia/Sakhalin|+11|-b0|0||58e4","Asia/Makassar|WITA|-80|0||15e5","Asia/Manila|PST|-80|0||24e6","Asia/Seoul|KST|-90|0||23e6","Asia/Rangoon|+0630|-6u|0||48e5","Asia/Tehran|+0330 +0430|-3u -4u|010101010|1XOIu 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0|14e6","Asia/Tokyo|JST|-90|0||38e6","Atlantic/Azores|-01 +00|10 0|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|25e4","Europe/Lisbon|WET WEST|0 -10|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|27e5","Atlantic/Cape_Verde|-01|10|0||50e4","Australia/Adelaide|ACDT ACST|-au -9u|01010101010101010101010|1XV4u 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0|11e5","Australia/Brisbane|AEST|-a0|0||20e5","Australia/Darwin|ACST|-9u|0||12e4","Australia/Eucla|+0845|-8J|0||368","Australia/Lord_Howe|+11 +1030|-b0 -au|01010101010101010101010|1XV30 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 
117821cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu|347","Australia/Perth|AWST|-80|0||18e5","Pacific/Easter|-05 -06|50 60|01010101010101010101010|1XVf0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0|30e2","Europe/Dublin|GMT IST|0 -10|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|12e5","Etc/GMT-1|+01|-10|0||","Pacific/Tongatapu|+13|-d0|0||75e3","Pacific/Kiritimati|+14|-e0|0||51e2","Etc/GMT-2|+02|-20|0||","Pacific/Tahiti|-10|a0|0||18e4","Pacific/Niue|-11|b0|0||12e2","Etc/GMT+12|-12|c0|0||","Pacific/Galapagos|-06|60|0||25e3","Etc/GMT+7|-07|70|0||","Pacific/Pitcairn|-08|80|0||56","Pacific/Gambier|-09|90|0||125","Etc/UTC|UTC|0|0||","Europe/London|GMT BST|0 -10|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|10e6","Europe/Chisinau|EET EEST|-20 -30|01010101010101010101010|1XSo0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|67e4","Europe/Moscow|MSK|-30|0||16e6","Europe/Volgograd|+04 MSK|-40 -30|01|249a0|10e5","Pacific/Honolulu|HST|a0|0||37e4","MET|MET MEST|-10 -20|01010101010101010101010|1XSp0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0|","Pacific/Chatham|+1345 +1245|-dJ -cJ|01010101010101010101010|1XV20 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0|600","Pacific/Apia|+14 +13|-e0 -d0|010101|1XV20 1a00 1fA0 1a00 1fA0|37e3","Pacific/Fiji|+13 +12|-d0 -c0|010101|1Xnq0 20o0 pc0 2hc0 bc0|88e4","Pacific/Guam|ChST|-a0|0||17e4","Pacific/Marquesas|-0930|9u|0||86e2","Pacific/Pago_Pago|SST|b0|0||37e2","Pacific/Norfolk|+11 +12|-b0 -c0|0101010101010101010101|219P0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0|25e4"],links:["Africa/Abidjan|Africa/Accra","Africa/Abidjan|Africa/Bamako","Africa/Abidjan|Africa/Banjul","Africa/Abidjan|Africa/Bissau","Africa/Abidjan|Africa/Conakry","Africa/Abidjan|Africa/Dakar","Africa/Abidjan|Africa/Freetown","Africa/Abidjan|Africa/Lome","Africa/Abidjan|Africa/Monrovia","Africa/Abidjan|Africa/Nouakchott","Africa/Abidjan|Africa/Ouagadougou","Africa/Abidjan|Africa/Timbuktu","Africa/Abidjan|America/Danmarkshavn","Africa/Abidjan|Atlantic/Reykjavik","Africa/Abidjan|Atlantic/St_Helena","Africa/Abidjan|Etc/GMT","Africa/Abidjan|Etc/GMT+0","Africa/Abidjan|Etc/GMT-0","Africa/Abidjan|Etc/GMT0","Africa/Abidjan|Etc/Greenwich","Africa/Abidjan|GMT","Africa/Abidjan|GMT+0","Africa/Abidjan|GMT-0","Africa/Abidjan|GMT0","Africa/Abidjan|Greenwich","Africa/Abidjan|Iceland","Africa/Algiers|Africa/Tunis","Africa/Cairo|Egypt","Africa/Casablanca|Africa/El_Aaiun","Africa/Johannesburg|Africa/Maseru","Africa/Johannesburg|Africa/Mbabane","Africa/Khartoum|Africa/Blantyre","Africa/Khartoum|Africa/Bujumbura","Africa/Khartoum|Africa/Gaborone","Africa/Khartoum|Africa/Harare","Africa/Khartoum|Africa/Kigali","Africa/Khartoum|Africa/Lubumbashi","Africa/Khartoum|Africa/Lusaka","Africa/Khartoum|Africa/Maputo","Africa/Khartoum|Africa/Windhoek","Africa/Lagos|Africa/Bangui","Africa/Lagos|Africa/Brazzaville","Africa/Lagos|Africa/Douala","Africa/Lagos|Africa/Kinshasa","Africa/Lagos|Africa/Libreville","Africa/Lagos|Africa/Luanda","Africa/Lagos|Africa/Malabo","Africa/Lagos|Africa/Ndjamena","Africa/Lagos|Africa/Niamey","Africa/Lagos|Africa/Porto-Novo","Africa/Nairobi|Africa/Addis_Ababa","Africa/Nairobi|Africa/Asmara","Africa/Nairobi|Africa/Asmera","Africa/Nairobi|Africa/Dar_es_Salaam","Africa/Nairobi|Africa/Djibouti","Africa/Nairobi|Africa/Kampala","Africa/Nairobi|Africa/Mogadishu","Africa/Nairobi|Indian/Antananarivo","Africa/Nairobi|Indian/Comoro","Africa/Nairobi|Indian/Mayotte","Africa/Tripoli|Europe/Kaliningrad","Africa/Tripoli|Libya","America/Adak|America/Atka","America/Adak|US/Aleutian","America/Anchorage|America/Juneau","America/Anchorage|America/Nome","America/Anchorage|America/Sitka","America/Anchorage|America/Yakutat","America/Anchorage|US/Alaska","America/Campo_Grande|America/Cuiaba","America/Caracas|America/Boa_Vista","America/Caracas|America/Guyana","America/Caracas|America/La_Paz","America/Caracas|America/Manaus","America/Caracas|America/Porto_Velho","America/Caracas|Brazil/West","America/Caracas|Etc/GMT+4","America/Chicago|America/Indiana/Knox","America/Chicago|America/Indiana/Tell_City","America/Chicago|America/Knox_IN","America/Chicago|America/Matamoros","America/Chicago|America/Menominee","America/Chicago|America/North_Dakota/Beulah","America/Chicago|America/North_Dakota/Center","America/Chicago|America/North_Dakota/New_Salem","America/Chicago|America/Rainy_River","America/Chicago|America/Rankin_Inlet","America/Chicago|America/Resolute","America/Chicago|America/Winnipeg","America/Chicago|CST6CDT","America/Chicago|Canada
11782/Central","America/Chicago|US/Central","America/Chicago|US/Indiana-Starke","America/Denver|America/Boise","America/Denver|America/Cambridge_Bay","America/Denver|America/Edmonton","America/Denver|America/Inuvik","America/Denver|America/Shiprock","America/Denver|America/Yellowknife","America/Denver|Canada/Mountain","America/Denver|MST7MDT","America/Denver|Navajo","America/Denver|US/Mountain","America/Fortaleza|America/Araguaina","America/Fortaleza|America/Argentina/Buenos_Aires","America/Fortaleza|America/Argentina/Catamarca","America/Fortaleza|America/Argentina/ComodRivadavia","America/Fortaleza|America/Argentina/Cordoba","America/Fortaleza|America/Argentina/Jujuy","America/Fortaleza|America/Argentina/La_Rioja","America/Fortaleza|America/Argentina/Mendoza","America/Fortaleza|America/Argentina/Rio_Gallegos","America/Fortaleza|America/Argentina/Salta","America/Fortaleza|America/Argentina/San_Juan","America/Fortaleza|America/Argentina/San_Luis","America/Fortaleza|America/Argentina/Tucuman","America/Fortaleza|America/Argentina/Ushuaia","America/Fortaleza|America/Bahia","America/Fortaleza|America/Belem","America/Fortaleza|America/Buenos_Aires","America/Fortaleza|America/Catamarca","America/Fortaleza|America/Cayenne","America/Fortaleza|America/Cordoba","America/Fortaleza|America/Jujuy","America/Fortaleza|America/Maceio","America/Fortaleza|America/Mendoza","America/Fortaleza|America/Montevideo","America/Fortaleza|America/Paramaribo","America/Fortaleza|America/Punta_Arenas","America/Fortaleza|America/Recife","America/Fortaleza|America/Rosario","America/Fortaleza|America/Santarem","America/Fortaleza|Antarctica/Palmer","America/Fortaleza|Antarctica/Rothera","America/Fortaleza|Atlantic/Stanley","America/Fortaleza|Etc/GMT+3","America/Godthab|America/Nuuk","America/Halifax|America/Glace_Bay","America/Halifax|America/Goose_Bay","America/Halifax|America/Moncton","America/Halifax|America/Thule","America/Halifax|Atlantic/Bermuda","America/Halifax|Canada/Atlantic","America/Havana|Cuba","America/Lima|America/Bogota","America/Lima|America/Eirunepe","America/Lima|America/Guayaquil","America/Lima|America/Porto_Acre","America/Lima|America/Rio_Branco","America/Lima|Brazil/Acre","America/Lima|Etc/GMT+5","America/Los_Angeles|America/Ensenada","America/Los_Angeles|America/Santa_Isabel","America/Los_Angeles|America/Tijuana","America/Los_Angeles|America/Vancouver","America/Los_Angeles|Canada/Pacific","America/Los_Angeles|Mexico/BajaNorte","America/Los_Angeles|PST8PDT","America/Los_Angeles|US/Pacific","America/Managua|America/Belize","America/Managua|America/Costa_Rica","America/Managua|America/El_Salvador","America/Managua|America/Guatemala","America/Managua|America/Regina","America/Managua|America/Swift_Current","America/Managua|America/Tegucigalpa","America/Managua|Canada/Saskatchewan","America/Mazatlan|Mexico/BajaSur","America/Mexico_City|America/Bahia_Banderas","America/Mexico_City|America/Merida","America/Mexico_City|America/Monterrey","America/Mexico_City|Mexico/General","America/New_York|America/Detroit","America/New_York|America/Fort_Wayne","America/New_York|America/Grand_Turk
vendor: 2,561 bytes, line 11782
11782","America/New_York|America/Indiana/Indianapolis","America/New_York|America/Indiana/Marengo","America/New_York|America/Indiana/Petersburg","America/New_York|America/Indiana/Vevay","America/New_York|America/Indiana/Vincennes","America/New_York|America/Indiana/Winamac","America/New_York|America/Indianapolis","America/New_York|America/Iqaluit","America/New_York|America/Kentucky/Louisville","America/New_York|America/Kentucky/Monticello","America/New_York|America/Louisville","America/New_York|America/Montreal","America/New_York|America/Nassau","America/New_York|America/Nipigon","America/New_York|America/Pangnirtung","America/New_York|America/Port-au-Prince","America/New_York|America/Thunder_Bay","America/New_York|America/Toronto","America/New_York|Canada/Eastern","America/New_York|EST5EDT","America/New_York|US/East-Indiana","America/New_York|US/Eastern","America/New_York|US/Michigan","America/Noronha|Atlantic/South_Georgia","America/Noronha|Brazil/DeNoronha","America/Noronha|Etc/GMT+2","America/Panama|America/Atikokan","America/Panama|America/Cancun","America/Panama|America/Cayman","America/Panama|America/Coral_Harbour","America/Panama|America/Jamaica","America/Panama|EST","America/Panama|Jamaica","America/Phoenix|America/Creston","America/Phoenix|America/Dawson_Creek","America/Phoenix|America/Fort_Nelson","America/Phoenix|America/Hermosillo","America/Phoenix|MST","America/Phoenix|US/Arizona","America/Santiago|Chile/Continental","America/Santo_Domingo|America/Anguilla","America/Santo_Domingo|America/Antigua","America/Santo_Domingo|America/Aruba","America/Santo_Domingo|America/Barbados","America/Santo_Domingo|America/Blanc-Sablon","America/Santo_Domingo|America/Curacao","America/Santo_Domingo|America/Dominica","America/Santo_Domingo|America/Grenada","America/Santo_Domingo|America/Guadeloupe","America/Santo_Domingo|America/Kralendijk","America/Santo_Domingo|America/Lower_Princes","America/Santo_Domingo|America/Marigot","America/Santo_Domingo|America/Martinique","America/Santo_Domingo|America/Montserrat","America/Santo_Domingo|America/Port_of_Spain","America/Santo_Domingo|America/Puerto_Rico","America/Santo_Domingo|America/St_Barthelemy","America/Santo_Domingo|America/St_Kitts","America/Santo_Domingo|America/St_Lucia","America/Santo_Domingo|America/St_Thomas","America/Santo_Domingo|America/St_Vincent","America/Santo_Domingo|America/Tortola","America/Santo_Domingo|America/Virgin","America/Sao_Paulo|Brazil/East","America/St_Johns|Canada/Newfoundland","America/Whitehorse|America/Dawson","America/Whitehorse|Canada/Yukon","Asia/
11782Almaty|Asia/Qostanay","Asia/Bangkok|Antarctica/Davis","Asia/Bangkok|Asia/Barnaul","Asia/Bangkok|Asia/Ho_Chi_Minh","Asia/Bangkok|Asia/Hovd","Asia/Bangkok|Asia/Krasnoyarsk","Asia/Bangkok|Asia/Novokuznetsk","Asia/Bangkok|Asia/Novosibirsk","Asia/Bangkok|Asia/Phnom_Penh","Asia/Bangkok|Asia/Saigon","Asia/Bangkok|Asia/Tomsk","Asia/Bangkok|Asia/Vientiane","Asia/Bangkok|Etc/GMT-7","Asia/Bangkok|Indian/Christmas","Asia/Chita|Asia/Dili","Asia/Chita|Asia/Khandyga","Asia/Chita|Asia/Yakutsk","Asia/Chita|Etc/GMT-9","Asia/Chita|Pacific/Palau","Asia/Dhaka|Asia/Bishkek","Asia/Dhaka|Asia/Dacca","Asia/Dhaka|Asia/Kashgar","Asia/Dhaka|Asia/Omsk","Asia/Dhaka|Asia/Thimbu","Asia/Dhaka|Asia/Thimphu","Asia/Dhaka|Asia/Urumqi","Asia/Dhaka|Etc/GMT-6","Asia/Dhaka|Indian/Chagos","Asia/Dubai|Asia/Baku","Asia/Dubai|Asia/Muscat","Asia/Dubai|Asia/Tbilisi","Asia/Dubai|Asia/Yerevan","Asia/Dubai|Etc/GMT-4","Asia/Dubai|Europe/Astrakhan","Asia/Dubai|Europe/Samara","Asia/Dubai|Europe/Saratov","Asia/Dubai|Europe/Ulyanovsk","Asia/Dubai|Indian/Mahe","Asia/Dubai|Indian/Mauritius","Asia/Dubai|Indian/Reunion","Asia/Gaza|Asia/Hebron","Asia/Hong_Kong|Hongkong","Asia/Jakarta|Asia/Pontianak","Asia/Jerusalem|Asia/Tel_Aviv","Asia/Jerusalem|Israel","Asia/Kamchatka|Asia/Anadyr","Asia/Kamchatka|Etc/GMT-12","Asia/Kamchatka|Kwajalein","Asia/Kamchatka|Pacific/Funafuti","Asia/Kamchatka|Pacific/Kwajalein","Asia/Kamchatka|Pacific/Majuro","Asia/Kamchatka|Pacific/Nauru","Asia/Kamchatka|Pacific/Tarawa","Asia/Kamchatka|Pacific/Wake","Asia/Kamchatka|Pacific/Wallis","Asia/Kathmandu|Asia/Katmandu","Asia/Kolkata|Asia/Calcutta","Asia/Kuala_Lumpur|Asia/Brunei","Asia/Kuala_Lumpur|Asia/Choibalsan","Asia/Kuala_Lumpur|Asia/Irkutsk","Asia/Kuala_Lumpur|Asia/Kuching","Asia/Kuala_Lumpur|Asia/Singapore","Asia/Kuala_Lumpur|Asia/Ulaanbaatar","Asia/Kuala_Lumpur|Asia/Ulan_Bator","Asia/Kuala_Lumpur|Etc/GMT-8","Asia/Kuala_Lumpur|Singapore","Asia/Makassar|Asia/Ujung_Pandang","Asia/Rangoon|Asia/Yangon","Asia/Rangoon|Indian/Cocos","Asia/Sakhalin|Asia/Magadan","Asia/Sakhalin|Asia/Srednekolymsk","Asia/Sakhalin|Etc/GMT-11","Asia/Sakhalin|Pacific/Bougainville","Asia/Sakhalin|Pacific/Efate","Asia/Sakhalin|Pacific/Guadalcanal","Asia/Sakhalin|Pacific/Kosrae","Asia/Sakhalin|Pacific/Noumea","Asia/Sakhalin|Pacific/Pohnpei","Asia/Sakhalin|Pacific/Ponape","Asia/Seoul|Asia/Pyongyang","Asia/Seoul|ROK","Asia/Shanghai|Asia/Chongqing","Asia/Shanghai|Asia/Chungking","Asia/Shanghai|Asia/Harbin","Asia/Shanghai|Asia/Macao","Asia/Shanghai|Asia/Macau","Asia/Shanghai|Asia/Taipei","Asia/Shanghai|PRC","Asia/Shanghai|ROC","Asia/Tashkent|Antarctica/Mawson","Asia/Tashkent|Asia/Aqtau","Asia/Tashkent|Asia/Aqtobe","Asia/Tashkent|Asia/Ashgabat","Asia/Tashkent|Asia/Ashkhabad","Asia/Tashkent|Asia/Atyrau","Asia/Tashkent|Asia/Dushanbe","Asia/Tashkent|Asia/Oral","Asia/Tashkent|Asia/Qyzylorda","Asia/Tashkent|Asia/Samarkand","Asia/Tashkent|Asia/Yekaterinburg","Asia/Tashkent|Etc/GMT-5","Asia/Tashkent|Indian/Kerguelen","Asia/Tashkent|Indian/Maldives","Asia/Tehran|Iran","Asia/Tokyo|Japan","Asia/Vladivostok|Antarctica/DumontDUrville","Asia/Vladivostok|Asia/Ust-Nera","Asia/Vladivostok|Etc/GMT-10","Asia/Vladivostok|Pacific/Chuuk","Asia/Vladivostok|Pacific/Port_Moresby","Asia/Vladivostok|Pacific/Truk","Asia/Vladivostok|Pacific/Yap","Atlantic/Cape_Verde|Etc/GMT+1","Australia/Adelaide|Australia/Broken_Hill","Australia/Adelaide|Australia/South","Australia/Adelaide|Australia/Yancowinna","Australia/Brisbane|Australia/Lindeman","Australia/Brisbane|Australia/Queensland","Australia/Darwin|Australia/North","Australia/Lord_Howe|Australia/LHI","Australia/Perth|Australia/West","Australia/Sydney|Antarctica/Macquarie","Australia/Sydney|Australia/ACT","Australia/Sydney|Australia/Canberra","Australia/Sydney|Australia/Currie","Australia/Sydney|Australia/Hobart","Australia/Sydney|Australia/Melbourne","Australia/Sydney|Australia/NSW","Australia/Sydney|Australia/Tasmania","Australia/Sydney|Australia/Victoria","Etc/UTC|Etc/UCT","Etc/UTC|Etc/Universal","Etc/UTC|Etc/Zulu","Etc/UTC|UCT","Etc/UTC|UTC","Etc/UTC|Universal","Etc/UTC|Zulu","Europe/Athens|Asia/Famagusta","Europe/Athens|Asia/Nicosia","Europe/Athens|EET","Europe/Athens|Europe/Bucharest","Europe/Athens|Europe/Helsinki","Europe/Athens|Europe/Kiev","Europe/Athens|Europe/Kyiv","Europe/Athens|Europe/Mariehamn","Europe/Athens|Europe/Nicosia","Europe/Athens|Europe/Riga","Europe/Athens|Europe/Sofia","Europe/Athens|Europe/Tallinn","Europe/Athens|Europe/Uzhgorod","Europe/Athens|Europe/Vilnius","Europe/Athens|Europe/Zaporozhye","Europe/Chisinau|Europe/Tiraspol","Europe/Dublin|Eire","Europe/Istanbul|Antarctica/Syowa","Europe/Istanbul|Asia/Aden","Europe/Istanbul|Asia/Baghdad","Europe/Istanbul|Asia/Bahrain","Europe/Istanbul|Asia/Istanbul","Europe/Istanbul|Asia/Kuwait","Europe/Istanbul|Asia/Qatar","Europe/Istanbul|Asia/Riyadh","Europe/Istanbul|Etc/GMT-3","Europe/Istanbul|Europe/Minsk","Europe/Istanbul|Turkey","Europe/Lisbon|Atlantic/Canary","Europe/Lisbon|Atlantic/Faeroe","Europe/Lisbon|Atlantic/Faroe","Europe/Lisbon|Atlantic/Madeira","Europe/Lisbon|Portugal","Europe/Lisbon|WET","Europe/London|Europe/Belfast","Europe/London|Europe/Guernsey","Europe/London|Europe/Isle_of_Man","Europe/London|Europe/Jersey","Europe/London|GB","Europe/London|GB-Eire","Europe/Moscow|Europe/Kirov","Europe/Moscow|Europe/Simferopol","Europe/Moscow|W-SU","Europe/Paris|Africa/Ceuta","Europe/Paris|Arctic/Longyearbyen","Europe/Paris|Atlantic/Jan_Mayen","Europe/Paris|CET","Europe/Paris|Europe/Amsterdam","Europe/Paris|Europe/Andorra","Europe/Paris|Europe/Belgrade","Europe/Paris|Europe/Berlin","Europe/Paris|Europe/Bratislava","Europe/Paris|Europe/Brussels","Europe/Paris|Europe/Budapest","Europe/Paris|Europe/Busingen","Europe/Paris|Europe/Copenhagen","Europe/Paris|Europe/Gibraltar","Europe/Paris|Europe/Ljubljana","Europe/Paris|Europe/Luxembourg","Europe/Paris|Europe/Madrid","Europe/Paris|Europe/Malta","Europe/Paris|Europe/Monaco","Europe/Paris|Europe/Oslo","Europe/Paris|Europe/Podgorica","Europe/Paris|Europe/Prague","Europe/Paris|Europe/Rome","Europe/Paris|Europe/San_Marino","Europe/Paris|Europe/Sarajevo","Europe/Paris|Europe/Skopje","Europe/Paris|Europe/Stockholm","Europe/Paris|Europe/Tirane","Europe/Paris|Europe/Vaduz","Europe/Paris|Europe/Vatican","Europe/Paris|Europe/Vienna","Europe/Paris|Europe/Warsaw","Europe/Paris|Europe/Zagreb","Europe/Paris|Europe/Zurich","Europe/Paris|Poland","Pacific/Auckland|Antarctica/McMurdo","Pacific/Auckland|Antarctica/South_Pole","Pacific/Auckland|NZ","Pacific/Chatham|NZ-CHAT","Pacific/Easter|Chile/EasterIsland","Pacific/Galapagos|Etc/GMT+6","Pacific/Gambier|Etc/GMT+9","Pacific/Guam|Pacific/Saipan","Pacific/Honolulu|HST","Pacific/Honolulu|Pacific/Johnston","Pacific/Honolulu|US/Hawaii","Pacific/Kiritimati|Etc/GMT-14","Pacific/Niue|Etc/GMT+11","Pacific/Pago_Pago|Pacific/Midway","Pacific/Pago_Pago|Pacific/Samoa","Pacific/Pago_Pago|US/Samoa","Pacific/Pitcairn|Etc/GMT+8","Pacific/Tahiti|Etc/GMT+10","Pacific/Tahiti|Pacific/Rarotonga","Pacific/Tongatapu|Etc/GMT-13","Pacific/Tongatapu|Pacific/Enderbury","Pacific/Tongatapu|Pacific/Fakaofo","Pacific/Tongatapu|Pacific/Kanton"],countries:["AD|Europe/Andorra","AE|Asia/Dubai","AF|Asia/Kabul","AG|America/Puerto_Rico America/Antigua","AI|America/Puerto_Rico America/Anguilla","AL|Europe/Tirane","AM|Asia/Yerevan","AO|Africa/Lagos Africa/Luanda","AQ|Antarctica/Casey Antarctica/Davis Antarctica/Mawson Antarctica/Palmer Antarctica/Rothera Antarctica/Troll Antarctica/Vostok Pacific/Auckland Pacific/Port_Moresby Asia/Riyadh Antarctica/McMurdo Antarctica/DumontDUrville Antarctica/Syowa
vendor: 9,310 bytes, line 11782
11782","AR|America/Argentina/Buenos_Aires America/Argentina/Cordoba America/Argentina/Salta America/Argentina/Jujuy America/Argentina/Tucuman America/Argentina/Catamarca America/Argentina/La_Rioja America/Argentina/San_Juan America/Argentina/Mendoza America/Argentina/San_Luis America/Argentina/Rio_Gallegos America/Argentina/Ushuaia","AS|Pacific/Pago_Pago","AT|Europe/Vienna","AU|Australia/Lord_Howe Antarctica/Macquarie Australia/Hobart Australia/Melbourne Australia/Sydney Australia/Broken_Hill Australia/Brisbane Australia/Lindeman Australia/Adelaide Australia/Darwin Australia/Perth Australia/Eucla","AW|America/Puerto_Rico America/Aruba","AX|Europe/Helsinki Europe/Mariehamn","AZ|Asia/Baku","BA|Europe/Belgrade Europe/Sarajevo","BB|America/Barbados","BD|Asia/Dhaka","BE|Europe/Brussels","BF|Africa/Abidjan Africa/Ouagadougou","BG|Europe/Sofia","BH|Asia/Qatar Asia/Bahrain","BI|Africa/Maputo Africa/Bujumbura","BJ|Africa/Lagos Africa/Porto-Novo","BL|America/Puerto_Rico America/St_Barthelemy","BM|Atlantic/Bermuda","BN|Asia/Kuching Asia/Brunei","BO|America/La_Paz","BQ|America/Puerto_Rico America/Kralendijk","BR|America/Noronha America/Belem America/Fortaleza America/Recife America/Araguaina America/Maceio America/Bahia America/Sao_Paulo America/Campo_Grande America/Cuiaba America/Santarem America/Porto_Velho America/Boa_Vista America/Manaus America/Eirunepe America/Rio_Branco","BS|America/Toronto America/Nassau","BT|Asia/Thimphu","BW|Africa/Maputo Africa/Gaborone","BY|Europe/Minsk","BZ|America/Belize","CA|America/St_Johns America/Halifax America/Glace_Bay America/Moncton America/Goose_Bay America/Toronto America/Iqaluit America/Winnipeg America/Resolute America/Rankin_Inlet America/Regina America/Swift_Current America/Edmonton America/Cambridge_Bay America/Inuvik America/Dawson_Creek America/Fort_Nelson America/Whitehorse America/Dawson America/Vancouver America/Panama America/Puerto_Rico America/Phoenix America/Blanc-Sablon America/Atikokan America/Creston","CC|Asia/Yangon Indian/Cocos","CD|Africa/Maputo Africa/Lagos Africa/Kinshasa Africa/Lubumbashi","CF|Africa/Lagos Africa/Bangui","CG|Africa/Lagos Africa/Brazzaville","CH|Europe/Zurich","CI|Africa/Abidjan","CK|Pacific/Rarotonga","CL|America/Santiago America/Punta_Arenas Pacific/Easter","CM|Africa/Lagos Africa/Douala","CN|Asia/Shanghai Asia/Urumqi","CO|America/Bogota","CR|America/Costa_Rica","CU|America/Havana","CV|Atlantic/Cape_Verde","CW|America/Puerto_Rico America/Curacao","CX|Asia/Bangkok Indian/Christmas","CY|Asia/Nicosia Asia/Famagusta","CZ|Europe/Prague","DE|Europe/Zurich Europe/Berlin Europe/Busingen","DJ|Africa/Nairobi Africa/Djibouti","DK|Europe/Berlin Europe/Copenhagen","DM|America/Puerto_Rico America/Dominica","DO|America/Santo_Domingo","DZ|Africa/Algiers","EC|America/Guayaquil Pacific/Galapagos","EE|Europe/Tallinn","EG|Africa/Cairo","EH|Africa/El_Aaiun","ER|Africa/Nairobi Africa/Asmara","ES|Europe/Madrid Africa/Ceuta Atlantic/Canary","ET|Africa/Nairobi Africa/Addis_Ababa","FI|Europe/Helsinki","FJ|Pacific/Fiji","FK|Atlantic/Stanley","FM|Pacific/Kosrae Pacific/Port_Moresby Pacific/Guadalcanal Pacific/Chuuk Pacific/Pohnpei","FO|Atlantic/Faroe","FR|Europe/Paris","GA|Africa/Lagos Africa/Libreville","GB|Europe/London","GD|America/Puerto_Rico America/Grenada","GE|Asia/Tbilisi","GF|America/Cayenne","GG|Europe/London Europe/Guernsey","GH|Africa/Abidjan Africa/Accra","GI|Europe/Gibraltar","GL|America/Nuuk America/Danmarkshavn America/Scoresbysund America/Thule","GM|Africa/Abidjan Africa/Banjul","GN|Africa/Abidjan Africa/Conakry","GP|America/Puerto_Rico America/Guadeloupe","GQ|Africa/Lagos Africa/Malabo","GR|Europe/Athens","GS|Atlantic/South_Georgia","GT|America/Guatemala","GU|Pacific/Guam","GW|Africa/Bissau","GY|America/Guyana","HK|Asia/Hong_Kong","HN|America/Tegucigalpa","HR|Europe/Belgrade Europe/Zagreb","HT|America/Port-au-Prince","HU|Europe/Budapest","ID|Asia/Jakarta Asia/Pontianak Asia/Makassar Asia/Jayapura","IE|Europe/Dublin","IL|Asia/Jerusalem","IM|Europe/London Europe/Isle_of_Man","IN|Asia/Kolkata","IO|Indian/Chagos","IQ|Asia/Baghdad","IR|Asia/Tehran","IS|Africa/Abidjan Atlantic/Reykjavik","IT|Europe/Rome","JE|Europe/London Europe/Jersey","JM|America/Jamaica","JO|Asia/Amman","JP|Asia/Tokyo","KE|Africa/Nairobi","KG|Asia/Bishkek","KH|Asia/Bangkok Asia/Phnom_Penh","KI|Pacific/Tarawa Pacific/Kanton Pacific/Kiritimati","KM|Africa/Nairobi Indian/Comoro","KN|America/Puerto_Rico America/St_Kitts","KP|Asia/Pyongyang","KR|Asia/Seoul","KW|Asia/Riyadh Asia/Kuwait","KY|America/Panama America/Cayman","KZ|Asia/Almaty Asia/Qyzylorda Asia/Qostanay Asia/Aqtobe Asia/Aqtau Asia/Atyrau Asia/Oral","LA|Asia/Bangkok Asia/Vientiane","LB|Asia/Beirut","LC|America/Puerto_Rico America/St_Lucia","LI|Europe/Zurich Europe/Vaduz","LK|Asia/Colombo","LR|Africa/Monrovia","LS|Africa/Johannesburg Africa/Maseru","LT|Europe/Vilnius","LU|Europe/Brussels Europe/Luxembourg","LV|Europe/Riga","LY|Africa/Tripoli","MA|Africa/Casablanca","MC|Europe/Paris Europe/Monaco","MD|Europe/Chisinau","ME|Europe/Belgrade Europe/Podgorica","MF|America/Puerto_Rico America/Marigot","MG|Africa/Nairobi Indian/Antananarivo","MH|Pacific/Tarawa Pacific/Kwajalein Pacific/Majuro","MK|Europe/Belgrade Europe/Skopje","ML|Africa/Abidjan Africa/Bamako","MM|Asia/Yangon","MN|Asia/Ulaanbaatar Asia/Hovd Asia/Choibalsan","MO|Asia/Macau","MP|Pacific/Guam Pacific/Saipan","MQ|America/Martinique","MR|Africa/Abidjan Africa/Nouakchott","MS|America/Puerto_Rico America/Montserrat","MT|Europe/Malta","MU|Indian/Mauritius","MV|Indian/Maldives","MW|Africa/Maputo Africa/Blantyre","MX|America/Mexico_City America/Cancun America/Merida America/Monterrey America/Matamoros America/Chihuahua America/Ciudad_Juarez America/Ojinaga America/Mazatlan America/Bahia_Banderas America/Hermosillo America/Tijuana","MY|Asia/Kuching Asia/Singapore Asia/Kuala_Lumpur","MZ|Africa/Maputo","NA|Africa/Windhoek","NC|Pacific/Noumea","NE|Africa/Lagos Africa/Niamey","NF|Pacific/Norfolk","NG|Africa/Lagos","NI|America/Managua","NL|Europe/Brussels Europe/Amsterdam","NO|Europe/Berlin Europe/Oslo","NP|Asia/Kathmandu","NR|Pacific/Nauru","NU|Pacific/Niue","NZ|Pacific/Auckland Pacific/Chatham","OM|Asia/Dubai Asia/Muscat","PA|America/Panama","PE|America/Lima","PF|Pacific/Tahiti Pacific/Marquesas Pacific/Gambier","PG|Pacific/Port_Moresby Pacific/Bougainville","PH|Asia/Manila","PK|Asia/Karachi","PL|Europe/Warsaw","PM|America/Miquelon","PN|Pacific/Pitcairn","PR|America/Puerto_Rico","PS|Asia/Gaza Asia/Hebron","PT|Europe/Lisbon Atlantic/Madeira Atlantic/Azores","PW|Pacific/Palau","PY|America/Asuncion","QA|Asia/Qatar","RE|Asia/Dubai Indian/Reunion","RO|Europe/Bucharest","RS|Europe/Belgrade","RU|Europe/Kaliningrad Europe/Moscow Europe/Simferopol Europe/Kirov Europe/Volgograd Europe/Astrakhan Europe/Saratov Europe/Ulyanovsk Europe/Samara Asia/Yekaterinburg Asia/Omsk Asia/Novosibirsk Asia/Barnaul Asia/Tomsk Asia/Novokuznetsk Asia/Krasnoyarsk Asia/Irkutsk Asia/Chita Asia/Yakutsk Asia/Khandyga Asia/Vladivostok Asia/Ust-Nera Asia/Magadan Asia/Sakhalin Asia/Srednekolymsk Asia/Kamchatka Asia/Anadyr","RW|Africa/Maputo Africa/Kigali","SA|Asia/Riyadh","SB|Pacific/Guadalcanal","SC|Asia/Dubai Indian/Mahe","SD|Africa/Khartoum","SE|Europe/Berlin Europe/Stockholm","SG|Asia/Singapore","SH|Africa/Abidjan Atlantic/St_Helena","SI|Europe/Belgrade Europe/Ljubljana","SJ|Europe/Berlin Arctic/Longyearbyen","SK|Europe/Prague Europe/Bratislava","SL|Africa/Abidjan Africa/Freetown","SM|Europe/Rome Europe/San_Marino","SN|Africa/Abidjan Africa/Dakar","SO|Africa/Nairobi Africa/Mogadishu","SR|America/Paramaribo","SS|Africa/Juba","ST|Africa/Sao_Tome","SV|America/El_Salvador","SX|America/Puerto_Rico America/Lower_Princes","SY|Asia/Damascus","SZ|Africa/Johannesburg Africa/Mbabane","TC|America/Grand_Turk","TD|Africa/Ndjamena","TF|Asia/Dubai Indian/Maldives Indian/Kerguelen","TG|Africa/Abidjan Africa/Lome","TH|Asia/Bangkok","TJ|Asia/Dushanbe","TK|Pacific/Fakaofo","TL|Asia/Dili","TM|Asia/Ashgabat","TN|Africa/Tunis","TO|Pacific/Tongatapu","TR|Europe/Istanbul","TT|America/Puerto_Rico America/Port_of_Spain","TV|Pacific/Tarawa Pacific/Funafuti","TW|Asia/Taipei","TZ|Africa/Nairobi Africa/Dar_es_Salaam","UA|Europe/Simferopol Europe/Kyiv","UG|Africa/Nairobi Africa/Kampala","UM|Pacific/Pago_Pago Pacific/Tarawa Pacific/Midway Pacific/Wake","US|America/New_York America/Detroit America/Kentucky/Louisville America/Kentucky/Monticello America/Indiana/Indianapolis America/Indiana/Vincennes America/Indiana/Winamac America/Indiana/Marengo America/Indiana/Petersburg America/Indiana/Vevay America/Chicago America/Indiana/Tell_City America/Indiana/Knox America/Menominee America/North_Dakota/Center America/North_Dakota/New_Salem America/North_Dakota/Beulah America/Denver America/Boise America/Phoenix America/Los_Angeles America/Anchorage America/Juneau America/Sitka America/Metlakatla America/Yakutat America/Nome America/Adak Pacific/Honolulu","UY|America/Montevideo","UZ|Asia/Samarkand Asia/Tashkent","VA|Europe/Rome Europe/Vatican","VC|America/Puerto_Rico America/St_Vincent","VE|America/Caracas","VG|America/Puerto_Rico America/Tortola","VI|America/Puerto_Rico America/St_Thomas","VN|Asia/Bangkok Asia/Ho_Chi_Minh","VU|Pacific/Efate","WF|Pacific/Tarawa Pacific/Wallis","WS|Pacific/Apia","YE|Asia/Riyadh Asia/Aden","YT|Africa/Nairobi Indian/Mayotte","ZA|Africa/Johannesburg","ZM|Africa
117822/Maputo Africa/Lusaka","ZW|Africa/Maputo Africa/Harare"]}),o});
11783global.EC_GLOBAL_MOMENT = global.moment; global.moment = global.moment_original || (delete global.moment, undefined);
11784})(typeof window !== 'undefined' ? window : global);
11785CAFE24.GLOBAL_DATETIME = (function() {
11786    var getDateTimeInfo = function (sInfo, mDefault) {
11787       if (typeof window.CAFE24.GLOBAL_DATETIME_INFO === 'object' && sInfo in window.CAFE24.GLOBAL_DATETIME_INFO) {
11788           return window.CAFE24.GLOBAL_DATETIME_INFO[sInfo];
11789       }
11790
11791       return mDefault;
11792    };
11793    var oConstants = getDateTimeInfo('oConstants', {});
11794    var oOptions = getDateTimeInfo('oOptions', {});
11795    var oPolicies = getDateTimeInfo('oPolicies', {});
11796    var sOverrideTimezone = getDateTimeInfo('sOverrideTimezone', '');
11797    var sMomentNamespace = getDateTimeInfo('sMomentNamespace', '');
11798
11799    var fMomentLoaded = function() {
11800        var bMomentLoaded = !!window[sMomentNamespace];
11801        var bMomentTZLoaded = false;
11802        if (bMomentLoaded) {
11803            bMomentTZLoaded = !!window[sMomentNamespace].tz;
11804        }
11805
11806        return bMomentLoaded && bMomentTZLoaded;
11807    };
11808
11809    var fMomentWrapper = function() {
11810        return window[sMomentNamespace];
11811    };
11812
11813    var fShallowMerge = function(oTarget, oSource) {
11814        oSource = oSource || {};
11815        for (var sKey in oSource) {
11816            if (oSource.hasOwnProperty(sKey)) {
11817                oTarget[sKey] = oSource[sKey];
11818            }
11819        }
11820
11821        return oTarget;
11822    };
11823
11824    var getFormatFromFlag = function(oOptions, iFlag, bOpposite) {
11825        if (bOpposite) {
11826            switch (iFlag) {
11827                case 1:
11828                    return oOptions[oConstants.IN_DATE_FORMAT];
11829                case 2:
11830                    return oOptions[oConstants.IN_TIME_FORMAT];
11831                default:
11832                    return oOptions[oConstants.IN_FORMAT];
11833            }
11834        }
11835
11836        switch (iFlag) {
11837            case 1:
11838                return oOptions[oConstants.OUT_DATE_FORMAT];
11839            case 2:
11840                return oOptions[oConstants.OUT_TIME_FORMAT];
11841            default:
11842                return oOptions[oConstants.OUT_FORMAT];
11843        }
11844    };
11845
11846    return {
11847        'const': oConstants,
11848
11849        init: function(fCallback) {
11850            if (fMomentLoaded()) {
11851                if (typeof fCallback === 'function') {
11852                    fCallback();
11853                }
11854
11855                return;
11856            }
11857
11858            var oScript = document.createElement('script');
11859            oScript.type = 'text/javascript';
11860            oScript.async = true;
11861            oScript.src = '/ind-script/moment.php?convert={$sConvert}';
11862            oScript.onload = oScript.onreadystatechange = function () {
11863                fMomentWrapper().defaultFormat = oOptions[oConstants.OUT_FORMAT];
11864                fMomentWrapper().tz.setDefault(oOptions[oConstants.IN_ZONE]);
11865
11866                if (typeof fCallback === 'function') {
11867                    fCallback();
11868                }
11869            };
11870
11871            var oFirstScript = document.getElementsByTagName('script')[0];
11872            oFirstScript.parentNode.insertBefore(oScript, oFirstScript);
11873        },
11874
11875        initPromise: function() {
11876            if (!window.Promise) {
11877                return;
11878            }
11879
11880            return new Promise(function(resolve) {
11881                this.init(resolve);
11882            }.bind(this));
11883        },
11884
11885        isLoaded: function() {
11886            return fMomentLoaded();
11887        },
11888
11889        setOptions: function(oNewOptions) {
11890            if (typeof oNewOptions === 'object') {
11891                for (var sKey in oNewOptions) {
11892                    if (oNewOptions.hasOwnProperty(sKey) && oOptions.hasOwnProperty(sKey)) {
11893                        oOptions[sKey] = oNewOptions[sKey];
11894                    }
11895                }
11896            }
11897
11898            return this;
11899        },
11900
11901        now: function(mOptions, iFlag) {
11902            if (fMomentLoaded() === false) {
11903                return Math.floor(new Date().getTime() / 1000);
11904            }
11905
11906            var oFormatOptions = this.getOptions(mOptions);
11907            return fMomentWrapper()()
11908                .tz(oFormatOptions.outZone)
11909                .format(getFormatFromFlag(oFormatOptions, iFlag));
11910        },
11911
11912        format: function(sTime, mOptions, iFlag) {
11913            if (fMomentLoaded() === false) {
11914                return sTime;
11915            }
11916
11917            var oFormatOptions = this.getOptions(mOptions);
11918            return fMomentWrapper()
11919                .tz(sTime, oFormatOptions.inZone)
11920                .tz(oFormatOptions.outZone)
11921                .format(getFormatFromFlag(oFormatOptions, iFlag));
11922        },
11923
11924        parse: function(sTime, mOptions) {
11925            if (fMomentLoaded() === false) {
11926                return sTime;
11927            }
11928
11929            var oParseOptions = this.getOptions(mOptions);
11930            return fMomentWrapper().tz((sTime || new Date()), oParseOptions.inZone).tz(oParseOptions.outZone);
11931        },
11932
11933        getOptions: function(mOptions, iFlag) {
11934            mOptions = mOptions || {};
11935
11936            var oMergedOptions = fShallowMerge({}, oOptions);
11937            if (typeof mOptions === 'string' && oPolicies[mOptions]) {
11938                oMergedOptions = fShallowMerge(oMergedOptions, oPolicies[mOptions]);
11939            } else if (typeof mOptions === 'object') {
11940                oMergedOptions = fShallowMerge(oMergedOptions, mOptions);
11941            }
11942
11943            if (sOverrideTimezone) {
11944                if ((typeof mOptions === 'string' && mOptions === 'shop') || (typeof mOptions === 'object' && !mOptions[oConstants.OUT_ZONE])) {
11945                    oMergedOptions[oConstants.OUT_ZONE] = sOverrideTimezone;
11946                }
11947            }
11948
11949            return oMergedOptions;
11950        },
11951
11952        getRevertOptions: function(mOptions) {
11953            var oCurrentOptions = this.getOptions(mOptions);
11954            var oMergedOptions = fShallowMerge({}, oOptions);
11955            oMergedOptions[oConstants.IN_ZONE] = oCurrentOptions[oConstants.OUT_ZONE];
11956            oMergedOptions[oConstants.IN_FORMAT] = oCurrentOptions[oConstants.OUT_FORMAT];
11957            oMergedOptions[oConstants.IN_DATE_FORMAT] = oCurrentOptions[oConstants.OUT_DATE_FORMAT];
11958            oMergedOptions[oConstants.IN_TIME_FORMAT] = oCurrentOptions[oConstants.OUT_TIME_FORMAT];
11959
11960            return oMergedOptions;
11961        },
11962
11963        today: function(sTime, mOptions, iFlag) {
11964            if (fMomentLoaded() === false) {
11965                throw new Error('MomentJS didnt initialize');
11966            }
11967
11968            mOptions = mOptions || 'shop';
11969            var oRevertOptions = this.getRevertOptions(mOptions);
11970            var oToday;
11971            if (!sTime || sTime === 'now') {
11972                oToday = this.parse('', mOptions);
11973            } else {
11974                iFlag = iFlag || oConstants.IN_FORMAT_ALL || 3;
11975                oToday = fMomentWrapper().tz(sTime, getFormatFromFlag(oRevertOptions, iFlag, true), oRevertOptions[oConstants.IN_ZONE]);
11976                if (oToday.isValid() === false) {
11977                    var oStandardDateRegex = new RegExp(oConstants.STANDARD_DATE_REGEX.replace(/\//g, ''));
11978                    if (oStandardDateRegex.test(sTime) === true) {
11979                        oToday = fMomentWrapper().tz(sTime, oRevertOptions[oConstants.IN_ZONE]);
11980                    } else {
11981                        oToday = fMomentWrapper()();
11982                    }
11983                }
11984            }
11985
11986            var oStartOfDay = oToday.clone().startOf('day');
11987            var oEndOfDay = oToday.clone().endOf('day');
11988
11989            var sStartOfDayInSeoul = oStartOfDay.tz(oConstants.SEOUL).format(
11989oConstants.FULL_TIME);
11990            var sEndOfDayInSeoul = oEndOfDay.tz(oConstants.SEOUL).format(oConstants.FULL_TIME);
11991
11992            return [sStartOfDayInSeoul, sEndOfDayInSeoul];
11993        },
11994
11995        parseFromFormat: function(sTime, mOptions, iFlag) {
11996            if (fMomentLoaded() === false) {
11997                return sTime;
11998            }
11999
12000            mOptions = mOptions || 'shop';
12001            iFlag = iFlag || oConstants.IN_FORMAT_ALL || 3;
12002
12003            var oRevertOptions = {};
12004            if (typeof mOptions === 'string') {
12005                oRevertOptions = this.getRevertOptions(mOptions);
12006            } else {
12007                oRevertOptions = this.getOptions(mOptions);
12008            }
12009
12010            return fMomentWrapper()(sTime, getFormatFromFlag(oRevertOptions, iFlag, true));
12011        }
12012    };
12013})();
12014
12015var EC_GLOBAL_DATETIME = CAFE24.getDeprecatedNamespace('EC_GLOBAL_DATETIME');
12016
12017!function() {
12018    'use strict';
12019    var re = {
12020        not_string: /[^s]/,
12021        not_bool: /[^t]/,
12022        not_type: /[^T]/,
12023        not_primitive: /[^v]/,
12024        number: /[diefg]/,
12025        numeric_arg: /[bcdiefguxX]/,
12026        json: /[j]/,
12027        not_json: /[^j]/,
12028        text: /^[^\x25]+/,
12029        modulo: /^\x25{2}/,
12030        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,
12031        key: /^([a-z_][a-z_\d]*)/i,
12032        key_access: /^\.([a-z_][a-z_\d]*)/i,
12033        index_access: /^\[(\d+)\]/,
12034        sign: /^[\+\-]/
12035    };
12036
12037    function sprintf(key) {
12038        try {
12039            // `arguments` is not an array, but should be fine for this call
12040            return sprintf_format(sprintf_parse(key), arguments);
12041        } catch (e) {
12042            return key;
12043        }
12044    }
12045
12046    function vsprintf(fmt, argv) {
12047        return sprintf.apply(null, [fmt].concat(argv || []));
12048    }
12049
12050    function sprintf_format(parse_tree, argv) {
12051        var cursor = 1, tree_length = parse_tree.length, arg, output = '', i, k, ph, pad, pad_character, pad_length, is_positive, sign;
12052        for (i = 0; i < tree_length; i++) {
12053            if (typeof parse_tree[i] === 'string') {
12054                output += parse_tree[i];
12055            }
12056            else if (typeof parse_tree[i] === 'object') {
12057                ph = parse_tree[i]; // convenience purposes only
12058                if (ph.keys) { // keyword argument
12059                    arg = argv[cursor];
12060                    for (k = 0; k < ph.keys.length; k++) {
12061                        if (arg == undefined) {
12062                            throw new Error(sprintf('[sprintf] Cannot access property "%s" of undefined value "%s"', ph.keys[k], ph.keys[k-1]));
12063                        }
12064                        arg = arg[ph.keys[k]];
12065                    }
12066                }
12067                else if (ph.param_no) { // positional argument (explicit)
12068                    arg = argv[ph.param_no];
12069                }
12070                else { // positional argument (implicit)
12071                    arg = argv[cursor++];
12072                }
12073
12074                if (re.not_type.test(ph.type) && re.not_primitive.test(ph.type) && arg instanceof Function) {
12075                    arg = arg();
12076                }
12077
12078                if (re.numeric_arg.test(ph.type) && (typeof arg !== 'number' && isNaN(arg))) {
12079                    throw new TypeError(sprintf('[sprintf] expecting number but found %T', arg));
12080                }
12081
12082                if (re.number.test(ph.type)) {
12083                    is_positive = arg >= 0;
12084                }
12085
12086                switch (ph.type) {
12087                    case 'b':
12088                        arg = parseInt(arg, 10).toString(2);
12089                        break;
12090                    case 'c':
12091                        arg = String.fromCharCode(parseInt(arg, 10));
12092                        break;
12093                    case 'd':
12094                    case 'i':
12095                        arg = parseInt(arg, 10);
12096                        break;
12097                    case 'j':
12098                        arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0);
12099                        break;
12100                    case 'e':
12101                        arg = ph.precision ? parseFloat(arg).toExponential(ph.precision) : parseFloat(arg).toExponential();
12102                        break;
12103                    case 'f':
12104                        arg = ph.precision ? parseFloat(arg).toFixed(ph.precision) : parseFloat(arg);
12105                        break;
12106                    case 'g':
12107                        arg = ph.precision ? String(Number(arg.toPrecision(ph.precision))) : parseFloat(arg);
12108                        break;
12109                    case 'o':
12110                        arg = (parseInt(arg, 10) >>> 0).toString(8);
12111                        break;
12112                    case 's':
12113                        arg = String(arg);
12114                        arg = (ph.precision ? arg.substring(0, ph.precision) : arg);
12115                        break;
12116                    case 't':
12117                        arg = String(!!arg);
12118                        arg = (ph.precision ? arg.substring(0, ph.precision) : arg);
12119                        break;
12120                    case 'T':
12121                        arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase();
12122                        arg = (ph.precision ? arg.substring(0, ph.precision) : arg);
12123                        break;
12124                    case 'u':
12125                        arg = parseInt(arg, 10) >>> 0;
12126                        break;
12127                    case 'v':
12128                        arg = arg.valueOf();
12129                        arg = (ph.precision ? arg.substring(0, ph.precision) : arg);
12130                        break;
12131                    case 'x':
12132                        arg = (parseInt(arg, 10) >>> 0).toString(16);
12133                        break;
12134                    case 'X':
12135                        arg = (parseInt(arg, 10) >>> 0).toString(16).toUpperCase();
12136                        break;
12137                }
12138                if (re.json.test(ph.type)) {
12139                    output += arg;
12140                }
12141                else {
12142                    if (re.number.test(ph.type) && (!is_positive || ph.sign)) {
12143                        sign = is_positive ? '+' : '-';
12144                        arg = arg.toString().replace(re.sign, '');
12145                    }
12146                    else {
12147                        sign = '';
12148                    }
12149                    pad_character = ph.pad_char ? ph.pad_char === '0' ? '0' : ph.pad_char.charAt(1) : ' ';
12150                    pad_length = ph.width - (sign + arg).length;
12151                    pad = ph.width ? (pad_length > 0 ? pad_character.repeat(pad_length) : '') : '';
12152                    output += ph.align ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg);
12153                }
12154            }
12155        }
12156        return output;
12157    }
12158
12159    var sprintf_cache = Object.create(null);
12160
12161    function sprintf_parse(fmt) {
12162        if (sprintf_cache[fmt]) {
12163            return sprintf_cache[fmt];
12164        }
12165
12166        var _fmt = fmt, match, parse_tree = [], arg_names = 0;
12167        while (_fmt) {
12168            if ((match = re.text.exec(_fmt)) !== null) {
12169                parse_tree.push(match[0]);
12170            }
12171            else if ((match = re.modulo.exec(_fmt)) !== null) {
12172                parse_tree.push('%');
12173            }
12174            else if ((match = re.placeholder.exec(_fmt)) !== null) {
12175                if (match[2]) {
12176                    arg_names |= 1;
12177                    var field_list = [], replacement_field = match[2], field_match = [];
12178                    if ((field_match = re.key.exec(replacement_field)) !== null) {
12179                        field_list.push(field_match[1]);
12180                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
12181                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
12182                                field_list.push(field_match[1]);
12183                            }
12184                            else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
12185                                field_list.push(field_match[1]);
12186                            }
12187                            else {
12188                                throw new SyntaxError('[sprintf] failed to parse named argument key');
12189                            }
12190                        }
12191                    }
12192                    else {
12193                        throw new SyntaxError('[sprintf] failed to parse named argument key');
12194                    }
12195                    match[2] = field_list;
12196                }
12197                else {
12198                    arg_names |= 2;
12199                }
12200                if (arg_names === 3) {
12201                    throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported');
12202                }
12203                parse_tree.push(
12204                    {
12205                        placeholder: match[0],
12206                        param_no: match[1],
12207                        keys: match[2],
12208                        sign: match[3],
12209                        pad_char: match[4],
12210                        align: match[5],
12211                        width: match[6],
12212                        precision: match[7],
12213                        type: match[8]
12214                    }
12215                );
12216            } else {
12217                throw new SyntaxError('[sprintf] unexpected placeholder');
12218            }
12219            _fmt = _fmt.substring(match[0].length);
12220        }
12221        return sprintf_cache[fmt] = parse_tree;
12222    }
12223
12224    /**
12225     * export to either browser or node.js
12226     */
12227    /* eslint-disable quote-props */
12228    if (typeof exports !== 'undefined') {
12229        exports['sprintf'] = sprintf;
12230        exports['vsprintf'] = vsprintf;
12231    }
12232    if (typeof window !== 'undefined') {
12233        window['sprintf'] = sprintf;
12234        window['vsprintf'] = vsprintf;
12235
12236        if (typeof define === 'function' && define['amd']) {
12237            define(function() {
12238                return {
12239                    'sprintf': sprintf,
12240                    'vsprintf': vsprintf
12241                };
12242            });
12243        }
12244    }
12245    /* eslint-enable quote-props */
12246}();
12247
12248/*
12249 * 각개체 별 항목 컨트롤 을 위해서 차후 확장을 고려 하여 별도로 추출
12250 * 
12251 */
12252var secondZipcodeHidden = function() {
12253    //Front Page 우편번호 2번째 엘레멘트 리스트
12254    var secondZipcodeElementId = ["postcode2", "rzipcode2", "ozipcode2", "zip2", "address_zip2"];
12255    for (var i in secondZipcodeElementId) {
12256        try {
12257            document.getElementById(secondZipcodeElementId[i]).style.display = "none";
12258        } catch (e) { }
12259    }
12260
12261    // 구디자인 회원 가입수정 zip2 제거
12262    try {
12263        document.frm.zip2.style.display = "none";
12264    } catch (e) { }
12265
12266    // 구디자인 배송목록 zip2 제거
12267    try {
12268        document.addr_set.rcv_zipcode2.style.display = "none";
12269    } catch (e) { }
12270
12271    // 구디자인 주문서 작성 zip2 제거
12272    try {
12273        document.frm.rzipcode2.style.display = "none";
12274        document.frm.ozipcode2.style.display = "none";
12275    } catch (e) { }
12276
12277    // 구디자인 세금계산서 신청약식 zip2 제거
12278    try {
12279        document.frm.mall_zipcode2.style.display = "none";
12280    } catch (e) { }
12281};
12282
12283secondZipcodeHidden();
12284
12285CAFE24.PLUSAPP_BRIDGE = (function() {
12286    var bUsePlusAppBridge = false;
12287
12288    return {
12289        /**
12290         * 해당 메소드를 사용해야 플러스앱에 데이터 전달 가능
12291         */
12292        setBridgeFunction: function () {
12293            bUsePlusAppBridge = true;
12294        },
12295
12296        /**
12297         * 플러스앱에 여러 데이터 전달
12298         * @param oBridgeData JSON 타입의 데이터
12299         */
12300        sendBridgeData: function (oBridgeData) {
12301            if (bUsePlusAppBridge && typeof (UsePlusAppBridge) !== undefined) {
12302                var browserInfo = navigator.userAgent;
12303                try {
12304                    if (browserInfo.indexOf("Cafe24Plus") > -1) {
12305                        var sBridgeData = JSON.stringify(oBridgeData);
12306
12307                        // Flutter 데이터 전송
12308                        if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
12309                            window.flutter_inappwebview.callHandler("sendDatasToApp", sBridgeData);
12310                            return;
12311                        } else {
12312                            if (window.flutter_inappwebview && window.flutter_inappwebview._callHandler) {
12313                                window.flutter_inappwebview._callHandler("sendDatasToApp", sBridgeData);
12314                                return;
12315                            }
12316                        }
12317
12318                        if (browserInfo.indexOf("android") > -1) {
12319                            if (window.PlusAppBridge.hasOwnProperty('sendDatasToApp') === true) {
12320                                window.PlusAppBridge.sendDatasToApp(sBridgeData);
12321                            }
12322                        } else if (typeof (webkit.messageHandlers.sendDatasToApp) !== 'undefined') {
12323                            webkit.messageHandlers.sendDatasToApp.postMessage(sBridgeData);
12324                        }
12325                    }
12326                } catch (e) {}
12327            }
12328        },
12329
12330        /**
12331         * 상품 번호 반환
12332         * @param sCheckedProduct
12333         */
12334        getProductNo: function (sCheckedProduct) {
12335            return sCheckedProduct.split(':')[0];
12336        },
12337
12338        /**
12339         * serialize 된 Form을 Json Object 포맷으로 변경
12340         * @param sParam
12341         */
12342        unserialize: function (sParam) {
12343            var objParam, aParam;
12344            aParam = sParam.replace(/\?/, "").split("&");
12345            objParam = {};
12346            EC$.each(aParam, function(iKey, sValue) {
12347                var aValue = sValue.split('=');
12348                return objParam[aValue[0]] = aValue[1];
12349            });
12350            return objParam;
12351        },
12352
12353        /**
12354         * 장바구니 등록 이벤트시 데이터 전송 처리
12355         * @param oParam
12356         */
12357        addBasket: function (oParam) {
12358            var oData = {
12359                type: 'basket',
12360                raw_data: oParam
12361            };
12362
12363            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oData);
12364        },
12365
12366        /**
12367         * 위시리스트 등록 이벤트시 데이터 전송 처리
12368         * @param iProductNo
12369         */
12370        addWishList: function (iProductNo) {
12371            var oData = {
12372                type: 'wish',
12373                raw_data: {
12374                    product_no: iProductNo
12375                }
12376            };
12377
12378            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oData);
12379        },
12380
12381        /**
12382         * SNS 링크 공유하기 이벤트시 데이터 전송 처리
12383         * @param {string} sMedia 소셜 미디어
12384         * @param {int} iProductNo 상품번호
12385         */
12386        shareSocialLink: function (sMedia, iProductNo) {
12387            var oData = {
12388                type: 'share',
12389                raw_data: {
12390                    method: sMedia,
12391                    product_no: iProductNo,
12392                }
12393            };
12394
12395            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oData);
12396        },
12397
12398        /**
12399         * 시리얼 쿠폰 등록에 성공시 데이터 전송 처리
12400         * @param string sCouponCode 시리얼 쿠폰 코드
12401         */
12402        addSerialCoupon: function (sCouponCode) {
12403            var oData = {
12404                type: 'coupon',
12405                raw_data: {
12406                    coupon_code: sCouponCode
12407                }
12408            };
12409
12410            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oData);
12411        },
12412
12413        /**
12414         * 주문 완료 후 데이터 전송 처리
12415         * @param object oParam 주문 완료 데이터
12416         */
12417        addOrderResult: function (oParam) {
12418            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oParam);
12419        },
12420
12421        /**
12422         * 프론트 주문서에서 결제 시작시 데이터 전송 처리
12423         * @param object oParam 결제 시작 데이터 (결제 금액, 통화 정보)
12424         */
12425        addBeginCheckout: function (oParam) {
12426            var oData = {
12427                type: 'begin_checkout',
12428                raw_data: oParam
12429            };
12430
12431            CAFE24.PLUSAPP_BRIDGE.sendBridgeData(oData);
12432        }
12433    };
12434})();
12435
12436var EC_PlusAppBridge = CAFE24.getDeprecatedNamespace('PLUSAPP_BRIDGE');
12437
12438CAFE24.UTIL = CAFE24.UTIL || {};
12439
12440// $.parseJSON 대체
12441CAFE24.UTIL.parseJSON = function(data) {
12442    if (typeof data !== "string" || !data) {
12443        return null;
12444    }
12445    return JSON.parse(data.trim());
12446};
12447
12448// $.trim 대체
12449CAFE24.UTIL.trim = function(text) {
12450    var trim = String.prototype.trim;
12451
12452    return text == null ? "" : trim.call(text);
12453};
12454
12455// $.browser 대체
12456CAFE24.UTIL.browser = (function() {
12457    var uaMatch = function(ua) {
12458        ua = ua.toLowerCase();
12459
12460        var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
12461            /(webkit)[ \/]([\w.]+)/.exec(ua) ||
12462            /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
12463            /(msie) ([\w.]+)/.exec(ua) ||
12464            ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
12465            [];
12466
12467        return {
12468            browser: match[1] || "",
12469            version: match[2] || "0"
12470        };
12471    };
12472
12473    matched = uaMatch(navigator.userAgent);
12474    browser = {};
12475
12476    if (matched.browser) {
12477        browser[matched.browser] = true;
12478        browser.version = matched.version;
12479    }
12480
12481    // Chrome is Webkit, but Webkit is also Safari.
12482    if (browser.chrome) {
12483        browser.webkit = true;
12484    } else if (browser.webkit) {
12485        browser.safari = true;
12486    }
12487    return browser;
12488})();
12489
12490
12491CAFE24.UTIL.topWindowJquery = function(selector) {
12492    if (window.top) {
12493        if (window.top.EC$) {
12494            return window.top.EC$(selector);
12495        }
12496        if (window.top.$) {
12497            return window.top.$(selector);
12498        }
12499    }
12500    return false;
12501};
12502
12503CAFE24.UTIL.parentWindowJquery = function(selector) {
12504    if (window.parent) {
12505        if (window.parent.EC$) {
12506            return window.parent.EC$(selector);
12507        }
12508        if (window.parent.$) {
12509            return window.parent.$(selector);
12510        }
12511    }
12512    return false;
12513};
12514
12515CAFE24.UTIL.openerWindowJquery = function(selector) {
12516    if (window.opener) {
12517        if (window.opener.EC$) {
12518            return window.opener.EC$(selector);
12519        }
12520        if (window.opener.$) {
12521            return window.opener.$(selector);
12522        }
12523    }
12524    return false;
12525};
12526
12527var EC_UTIL = CAFE24.getDeprecatedNamespace('EC_UTIL');
12528
12529/* 사용자 안내띠 버튼 리사이징 */
12530EC$(function() {
12531    window.addEventListener("resize", handleEvent);
12532    window.addEventListener('scroll', handleEvent);
12533
12534    handleEvent();
12535
12536    function handleEvent(){
12537        var targetEl = document.querySelectorAll('.certifyBox');
12538        var bodyWidth = document.body.offsetWidth,
12539            winWidth = window.innerWidth-16,
12540            rightNum = 48;
12541
12542        for(var i=0; i<targetEl.length; i++){
12543            var btnElChk = targetEl[i].querySelector('.btnTxt span');
12544            if(btnElChk){
12545                var btnWidth = btnElChk.offsetWidth;
12546                if(winWidth < 600) winWidth = 600;
12547                var left = (winWidth-(btnWidth+rightNum)+window.scrollX),
12548                    cul = bodyWidth - winWidth;
12549
12550                if(bodyWidth > winWidth && window.scrollX <= cul) {
12551                    targetEl[i].querySelector('.btnTxt').style.left = left + "px";
12552                }else{
12553                    targetEl[i].querySelector('.btnTxt').style.left = "";
12554                }
12555            }
12556        }
12557    }
12558});
12559EC$(function () {
12560    var oAgent = navigator.userAgent.toLowerCase();
12561    if (!EC$.cookie('ec_ipad_device') || /[TF]/.exec(EC$.cookie('ec_ipad_device')) == undefined) {
12562        if ((oAgent.indexOf('macintosh') != -1 && 'ontouchend' in document) === true) {
12563            EC$.cookie('ec_ipad_device', 'T', {path: '/'});
12564        } else { EC$.cookie('ec_ipad_device', 'F', {path: '/'}); }
12565    }
12566
12567    // 장바구니 link에 ch_ref 파라미터 추가
12568    if (typeof CAFE24 !== 'undefined' && CAFE24.hasChRef && CAFE24.hasChRef()) {
12569        $('a[href*="/order/basket.html"]').each(function () {
12570            try {
12571                const $link = $(this);
12572                const href = $link.attr("href");
12573
12574                // href가 유효한지 확인
12575                if (!href || href.startsWith('javascript:') || href.startsWith('mailto:')) {
12576                    return;
12577                }
12578
12579                // URL 파싱 (IE 호환성 고려)
12580                let url;
12581                try {
12582                    url = new URL(href, window.location.origin);
12583                } catch (e) {
12584                    // IE 호환성을 위한 폴백
12585                    const link = document.createElement('a');
12586                    link.href = href;
12587                    url = {
12588                        pathname: link.pathname,
12589                        search: link.search,
12590                        hash: link.hash,
12591                        searchParams: {
12592                            set: function(key, value) {
12593                                const params = new URLSearchParams(this.toString());
12594                                params.set(key, value);
12595                                this.toString = function() { return params.toString(); };
12596                            }
12597                        }
12598                    };
12599                }
12600
12601                const chRef = CAFE24.getChRefData && CAFE24.getChRefData();
12602                if (chRef) {
12603                    url.searchParams.set('ch_ref', chRef);
12604                    $link.attr("href", url.pathname + url.search + url.hash);
12605                }
12606            } catch (error) {
12607                console.warn('Error processing basket link:', error);
12608            }
12609        });
12610    }
12611});
12612
12613CAFE24.FRONT_XANS_INTERPRETER = (function() {
12614    // 변수 정규표현식
12615    var XANS_VAR_FULL_NAME_REGEXP = '\\{\\$([a-z0-9_\\.]+)(?:[\\s]*[\\|][\\s]*([a-z0-9]+)[\\s]*[:]?((?:[^\\{\\}]+)*))?\\}';
12616
12617    // 템플릿에서 모든 변수를 찾기 위한 정규식
12618    var regexpFindSDEVarFullName = new RegExp(XANS_VAR_FULL_NAME_REGEXP, 'ig');
12619
12620    // '{$var_name|display}'과 같은 문자열에서 변수명과 모디파이어를 분리하기 위한 정규식
12621    var regexpSDEVarFullname = new RegExp('^' + XANS_VAR_FULL_NAME_REGEXP + '$', 'i');
12622
12623    // 모디파이어
12624    var aSDEModifier = {
12625        display: function(sVar)
12626        {
12627            if (sVar) {
12628                return '';
12629            } else {
12630                return 'displaynone';
12631            }
12632        },
12633        numberformat: function(sVar)
12634        {
12635            if (isFinite(sVar)) {
12636                return number_format(sVar);
12637            } else {
12638                return '';
12639            }
12640        },
12641        striptag: function(sVar)
12642        {
12643            return sVar.replace(/(<([^>]+)>)/ig, '');
12644        }
12645    };
12646
12647    /**
12648     * 숫자를 3자리씩 콤마(,)로 끊어서 문자열로 변환하여 리턴합니다.
12649     * @param string sNumber 숫자
12650     * @returns {string} 콤마 반영된 문자열
12651     */
12652    function number_format(sNumber)
12653    {
12654        // 3자리씩 ,로 끊어서 리턴
12655        var sNumber = String(parseInt(sNumber));
12656        var regexp = /^(-?[0-9]+)([0-9]{3})($|\.|,)/;
12657        while (regexp.test(sNumber)) {
12658            sNumber = sNumber.replace(regexp, "$1,$2$3");
12659        }
12660        return sNumber;
12661    }
12662
12663    /**
12664     * 전체 변수명에서 실제 변수명과 모디파이어 등을 분리하여 리턴합니다.
12665     * @param string sVarFullName '{$var_name|display}' 형태의 전체 변수명
12666     * @returns {{var_name: *, modifire: *}}
12667     */
12668    function parseVariableInfo(sVarFullName)
12669    {
12670        var aMatches = sVarFullName.match(regexpSDEVarFullname);
12671
12672        return {
12673            var_name: aMatches[1],
12674            modifire: aMatches[2]
12675        };
12676    }
12677
12678    /**
12679     * XANS 템플릿에서 변수를 반영하여 리턴합니다.
12680     * @param string sTemplate 템플릿 (HTML)
12681     * @param array aVars 변수 리스트
12682     * @return string 완성된 HTML
12683     */
12684    function fetch(sTemplate, aVars)
12685    {
12686        var aHtml = sTemplate.split('<!--#-->');
12687        var sHtml = '';
12688
12689        EC$(aHtml).each(function(iIndex, sModuleHtml) {
12690            if (iIndex < 1 || (iIndex % 2) !== 1) {
12691                sHtml += convertHtmlVars(sModuleHtml, aVars);
12692            } else {
12693                var oObj = EC$(sModuleHtml);
12694                var sChildNode = EC$('<div>').append(oObj.children().first().clone()).html();
12695                if (/<!--\$--><!--@-->([\s\S]+)<!--\$-->/gm.test(sModuleHtml) === true) {
12696                    sChildNode = /<!--\$--><!--@-->([\s\S]+)<!--\$-->/gm.exec(sModuleHtml)[1].split('<!--@-->')[0];
12697                }
12698                var sModuleClass = EC$(oObj).attr('class');
12699                var sModuleName = ucfirst(sModuleClass.match(/xans-product-([^- ]+)/)[1]);
12700
12701                if (typeof(aVars['@' + sModuleName]) === 'object') {
12702                    var s = '';
12703                    EC$(aVars['@' + sModuleName]).each(function(i, aData) {
12704                        s += convertHtmlVars(sChildNode, aData);
12705                    });
12706
12707                    if (s !== '') {
12708                        sHtml += EC$('<div>').append(oObj.html(s).clone()).html();
12709                    }
12710                }
12711            }
12712        });
12713
12714        return sHtml;
12715    }
12716
12717    function ucfirst(sString)
12718    {
12719        if (typeof(sString) !== 'string') {
12720            return '';
12721        }
12722        return sString.substring(0, 1).toUpperCase() + sString.substring(1).toLowerCase();
12723    }
12724
12725    function convertHtmlVars(sTemplate, aVars)
12726    {
12727        return sTemplate.replace(regexpFindSDEVarFullName, function(sVarFullName) {
12728            var aVarInfo = parseVariableInfo(sVarFullName);
12729
12730            var sValue = '';
12731            if (aVars[aVarInfo.var_name] || aVars[aVarInfo.var_name] === 0) {
12732                sValue = aVars[aVarInfo.var_name];
12733            }
12734
12735            if (aVarInfo.modifire !== undefined && aSDEModifier.hasOwnProperty(aVarInfo.modifire) === true) {
12736                return aSDEModifier[aVarInfo.modifire](sValue);
12737
12738            } else {
12739                return sValue;
12740
12741            }
12742        });
12743    }
12744
12745    /**
12746     * XANS 템플릿에서 변수 리스트를 얻어서 리턴합니다.
12747     * @param string sTemplate 템플릿 (HTML)
12748     * @return array 변수 리스트 (ex: ['{$var_name}', '{$var_name|display}'])
12749     */
12750    function getVariables(sTemplate)
12751    {
12752        return sTemplate.match(regexpFindSDEVarFullName);
12753    }
12754
12755    return {
12756        getVariables: getVariables,
12757        parseVariableInfo: parseVariableInfo,
12758        fetch: fetch
12759    };
12760})();
12761
12762var EC_FRONT_XANS_INTERPRETER = CAFE24.getDeprecatedNamespace('EC_FRONT_XANS_INTERPRETER');
12763
12764CAFE24.FRONT_XANS_TEMPLATE = (function() {
12765    // 모듈별 템플릿
12766    var aModuleTemplates = {};
12767
12768    /**
12769     * 모듈별 템플릿을 셋팅합니다.
12770     * @param string sModuleName 모듈명 (xans-product-listmain-1)
12771     * @param string sModuleTemplate 모듈 템플릿
12772     */
12773    function setTemplate(sModuleName, sModuleTemplate)
12774    {
12775        aModuleTemplates[sModuleName] = sModuleTemplate;
12776
12777        if (/^xans-product-list|^xans-product-hashtaglist/.test(sModuleName) === true) {
12778            var sTemplateForVDOM = getTemplateForVDOM(sModuleName);
12779            var $li = EC$(sTemplateForVDOM).find('li').first();
12780            var sLiHTMLForVDOM = EC$('<ul>').append($li).html();
12781
12782            // 해시태그 모듈에 대한 별도 캐싱 처리
12783            if (/^xans-product-hashtaglist/.test(sModuleName) === true) {
12784                aModuleTemplates[sModuleName] = convertVDomHtmlToHTML(sLiHTMLForVDOM);
12785            } else {
12786                // oMobileDomData를 여전히 사용중인 사용자js와의 호환성을 위한 예외처리 - ECHOSTING-142586
12787                window.oMobileDomData = {
12788                    dom: convertVDomHtmlToHTML(sLiHTMLForVDOM),
12789                    data: CAFE24.FRONT_XANS_INTERPRETER.getVariables(sLiHTMLForVDOM)
12790                };
12791            }
12792        }
12793    }
12794
12795    /**
12796     * 모듈별 템플릿을 가져옵니다.
12797     * @param string sModuleName 모듈명
12798     * @return string 모듈별 템플릿
12799     */
12800    function getTemplate(sModuleName)
12801    {
12802        if (aModuleTemplates.hasOwnProperty(sModuleName)) {
12803            return aModuleTemplates[sModuleName];
12804        } else {
12805            return undefined;
12806        }
12807    }
12808
12809    /**
12810     * Virtual DOM 에서 사용할 모듈별 템플릿을 가져옵니다.
12811     * @param string sModuleName 모듈명
12812     * @return string 모듈별 템플릿
12813     */
12814    function getTemplateForVDOM(sModuleName)
12815    {
12816        var sTemplate = getTemplate(sModuleName) || '';
12817
12818        // src 속성에 대해 "//:0" 처리해줍니다.
12819        var sTemplateForVDOM = sTemplate.replace(/(\s+src\s*=\s*["'])/g, '$1//:0#xansjs');
12820
12821        return sTemplateForVDOM;
12822    }
12823
12824    /**
12825     * "Virtual DOM"용 HTML을 일반 HTML로 변환하여 리턴합니다.
12826     * @param string sTemplateForVDOM "Virtual DOM"용 사용한 템플릿 HTML
12827     * @return string 일반 HTML
12828     */
12829    function convertVDomHtmlToHTML(sTemplateForVDOM)
12830    {
12831        // src 속성에서 "//:0#xansjs"를 삭제합니다.
12832        var sTemplate = sTemplateForVDOM.replace(/(\s+src\s*=\s*["'])\/\/:0#xansjs/g, '$1');
12833
12834        return sTemplate;
12835    }
12836
12837    return {
12838        setTemplate: setTemplate,
12839        getTemplate: getTemplate,
12840        getTemplateForVDOM: getTemplateForVDOM,
12841        convertVDomHtmlToHTML: convertVDomHtmlToHTML
12842    };
12843})();
12844
12845var EC_FRONT_XANS_TEMPLATE = CAFE24.getDeprecatedNamespace('EC_FRONT_XANS_TEMPLATE');
12846
12847/**
12848 * 모바일 전용 Util
12849 */
12850CAFE24.MOBILE_UTIL = {
12851    /*
12852     * get li
12853     */
12854    convertNode: function(node) {
12855        return CAFE24.FRONT_XANS_INTERPRETER.fetch(oMobileDomData.dom, node);
12856    },
12857
12858    /*
12859     * set default img
12860     */
12861    setDefaultImage: function() {
12862        EC$(".thumbnail img,img.ThumbImage,img.BigImage").each(function($i,$item) {
12863            var $img = new Image();
12864            $img.onerror = function () {
12865                    $item.src="//img.echosting.cafe24.com/thumb/img_product_big.gif";
12866            };
12867            $img.src = this.src;
12868        });
12869    },
12870
12871    /*
12872     * get ajax url
12873     */
12874    getAjaxUrl: function(sModule) {
12875        var aAjax = [];
12876
12877        aAjax['xans-product-listnormal'] = '/exec/front/Product/ApiProductNormal';
12878        aAjax['xans-product-listmain'] = '/exec/front/Product/ApiProductMain';
12879
12880        return aAjax[sModule];
12881    },
12882
12883    /*
12884     * set param
12885     */
12886    setAjaxParam: function(aData, sModule) {
12887        var aParam = [];
12888
12889        if (typeof(aData['cate_no']) === 'number' && aData['cate_no'] > 0) { aParam.push('cate_no=' + aData['cate_no']); }
12890        if (typeof(aData['display_group']) === 'number' && aData['display_group'] > 0) { aParam.push('display_group=' + aData['display_group']); }
12891        if (typeof(aData['sort_method']) === 'number' && aData['sort_method'] > 0) { aParam.push('sort_method=' + aData['sort_method']); }
12892        if (typeof(aData['supplier_code']) === 'string' && aData['supplier_code'] !== '') { aParam.push('supplier_code=' + aData['supplier_code']); }
12893        if (typeof(aData['ec_soldout_display']) === 'string' && aData['ec_soldout_display'] !== '') { aParam.push('ec_soldout_display=' + aData['ec_soldout_display']); }
12894        aParam.push('page=' + aData['page']);
12895        aParam.push('bInitMore=' + aData['bInitMore']);
12896        aParam.push('count=' + aData['count']);
12897
12898        return this.getAjaxUrl(sModule) + '?' + aParam.join('&');
12899    }
12900};
12901
12902var EC_MOBILE_UTIL = CAFE24.getDeprecatedNamespace('EC_MOBILE_UTIL');
12903
12904/*
12905 * Swipe 1.0
12906 *
12907 * Brad Birdsall, Prime
12908 * Copyright 2011, Licensed GPL & MIT
12909 *
12910*/
12911window.SwipeClient = function(element, options) {
12912    // return immediately if element doesn't exist
12913    if (! element) {
12914        return null;
12915    }
12916
12917    var _this = this;
12918
12919    // retreive options
12920    this.options = options || {};
12921    this.index = this.options.startSlide || 0;
12922    this.speed = this.options.speed || 300;
12923    this.callback = this.options.callback || function() {};
12924    this.delay = this.options.auto || 0;
12925    this.postback = this.options.postback || true;
12926
12927    // 캐싱 사용유무 (기본값으로 이미 넘어오지만 그래도 no로 한번 더 저장)
12928    this.cache = this.options.cache || 'no';
12929
12930    // 현재 슬라이드 개별 모듈의 순서(인덱스)를 저장하기 위해 저장 (상품번호, 카테코리 번호 등으로 조합된 상품별 유니크 값)
12931    this.storageId = this.options.elementId || '';
12932
12933    // reference dom elements
12934    this.container = element;
12935    this.element = this.container.getElementsByTagName('ul')[0]; // the slide pane
12936
12937    // static css
12938    this.container.style.overflow = 'hidden';
12939    this.element.style.listStyle = 'none';
12940    this.element.style.margin = 0;
12941
12942    // trigger slider initialization
12943    this.setup();
12944
12945    // begin auto slideshow
12946    this.begin();
12947
12948    // add event listeners
12949    if (this.element.addEventListener) {
12950        this.element.addEventListener('touchstart', this, false);
12951        this.element.addEventListener('touchmove', this, false);
12952        this.element.addEventListener('touchend', this, false);
12953        this.element.addEventListener('touchcancel', this, false);
12954        this.element.addEventListener('webkitTransitionEnd', this, false);
12955        this.element.addEventListener('msTransitionEnd', this, false);
12956        this.element.addEventListener('oTransitionEnd', this, false);
12957        this.element.addEventListener('transitionend', this, false);
12958
12959        window.addEventListener('resize', this, false);
12960    }
12961};
12962
12963SwipeClient.prototype = {
12964    setup: function() {
12965        // get and measure amt of slides
12966        this.slides = this.element.children;
12967        this.length = this.slides.length;
12968
12969        // return immediately if their are less than two slides
12970        if (this.length < 2) {
12971            return null;
12972        }
12973
12974        // determine width of each slide
12975        this.width = Math.ceil(('getBoundingClientRect' in this.container) ? this.container.getBoundingClientRect().width : this.container.offsetWidth);
12976
12977        // Fix width for Android WebView (i.e. PhoneGap)
12978        if (this.width === 0 && typeof window.getComputedStyle === 'function') {
12979            this.width = window.getComputedStyle(this.container, null).width.replace('px','');
12980        }
12981
12982        // return immediately if measurement fails
12983        if (! this.width) {
12984            return null;
12985        }
12986
12987        // hide slider element but keep positioning during setup
12988        var origVisibility = this.container.style.visibility;
12989
12990        this.container.style.visibility = 'hidden';
12991
12992        // dynamic css
12993        this.element.style.width = Math.ceil(this.slides.length * this.width) + 'px';
12994
12995        var index = this.slides.length;
12996
12997        while (index--) {
12998            var el = this.slides[index];
12999
13000            el.style.width = this.width + 'px';
13001            el.style.display = 'table-cell';
13002            el.style.verticalAlign = 'top';
13003        }
13004
13005        // set start position and force translate to remove initial flickering
13006
13007        // 캐싱 사용중일 경우에만 처리
13008        if (this.cache === 'yes') {
13009            // 저장된 세선 스토리지 읽어와 처리
13010            // 각 스와이프의 개별 모듈에 해당되는 세션 스토리지 값
13011            // NaN 보다는 parseInt(null)로 명확하게 구분
13012            var iStorageIndexData = parseInt(null);
13013
13014            // 상품 상세페이지에서 생성된 세션 스토리지 키
13015            var sStorageDetailName = 'sStorageDetail';
13016
13017            // 상품 상세페이지에서 생성된 세션 스토리지 값 (Unix Timestamp)
13018            // NaN 보다는 parseInt(null)로 명확하게 구분
13019            var iStorageDetailData = parseInt(null);
13020
13021            // 현재 시간 Unix Timstamp
13022            var iNowTime = Math.floor(new Date().getTime() / 1000);
13023
13024            // 세션 스토리지 유지 시간
13025            var iSessionTime = 60 * 5;
13026
13027            // 값 할당 (int)
13028            try {
13029                iStorageIndexData = parseInt(sessionStorage.getItem(this.storageId));
13030                iStorageDetailData = parseInt(sessionStorage.getItem(sStorageDetailName));
13031            } catch (e) {
13032            }
13033
13034            // 저장된 값(추가 이미지)이 삭제된 경우 빈 페이지로 스와이프 되므로, 저장된 인덱스에 해당되는 이미지가 없는 경우는 세션 스토리지 삭제
13035            if (typeof($S.aButton[iStorageIndexData]) === 'undefined') {
13036                // 할당된 값 초기화
13037                iStorageIndexData = parseInt(null);
13038
13039                // 실제 세션 스토리지에서도 삭제
13040                try {
13041                    sessionStorage.removeItem(this.storageId);
13042                } catch (e) {
13043                }
13044            }
13045
13046            // 값이 있다면 moveTab을 해야 Circle(페이징 원)까지 변경됨
13047            // 상세페이지에서 생성된 세션 스토리지가 특정 시간이 경과하지 않은 경우에만 처리
13048            // 만약 모듈에서 상품번호 등의 정보(this.storageId)를 가져ì˜
13048¤ì§€ 못한 경우에는 처리하지 않음
13049            if (this.storageId !== '' && isNaN(iStorageIndexData) === false && isNaN(iStorageDetailData) === false && iStorageDetailData + iSessionTime >= iNowTime) {
13050                // 실제 이동 처리
13051                this.moveTab(iStorageIndexData, 0);
13052            } else {
13053                this.slide(this.index, 0);
13054            }
13055        } else {
13056            this.slide(this.index, 0);
13057        }
13058
13059        this.container.style.visibility = origVisibility;
13060    },
13061
13062    slide: function(index, duration) {
13063        // if useing ajax load
13064        try {
13065            if (oMobileSliderData.sPictorialLoad === true) {
13066                if ($S.iAjax === index + 1 && $S.bAjax === true) {
13067                    $S.callAjax();
13068                }
13069            }
13070        } catch (e) {}
13071
13072        var style = this.element.style;
13073
13074        // fallback to default speed
13075        if (duration == undefined) {
13076          duration = this.speed;
13077        }
13078
13079        // set duration speed (0 represents 1-to-1 scrolling)
13080        style.webkitTransitionDuration = style.MozTransitionDuration = style.msTransitionDuration = style.OTransitionDuration = style.transitionDuration = duration + 'ms';
13081
13082        // translate to given index position
13083        style.MozTransform = style.webkitTransform = 'translate3d(' + -(index * this.width) + 'px, 0, 0)';
13084        style.msTransform = style.OTransform = 'translateX(' + -(index * this.width) + 'px)';
13085
13086        // set new index to allow for expression arguments
13087        this.index = index;
13088
13089        // 현재 모듈의 인덱스를 세선 스토리지에 저장
13090        // 캐시 사용중이며 인덱스가 있으면서 저장할 스토리지 ID 값이 있는 경우에만 처리
13091        if (this.cache === 'yes' && isNaN(this.index) === false && this.storageId !== '') {
13092            try {
13093                sessionStorage.setItem(this.storageId, this.index);
13094            } catch (e) {
13095            }
13096        }
13097    },
13098
13099    getPos: function() {
13100        // return current index position
13101        return this.index;
13102    },
13103
13104    prev: function(delay, postback) {
13105        // cancel next scheduled automatic transition, if any
13106        this.delay = delay || 0;
13107        this.postback = (postback == undefined) ? true : postback;
13108
13109        clearTimeout(this.interval);
13110
13111        if (this.index) {
13112            this.slide(this.index - 1, this.speed);
13113        } else {
13114            if (this.postback !== false) {
13115                this.slide(this.length - 1, this.speed); //if first slide return to end
13116            }
13117        }
13118    },
13119
13120    next: function(delay, postback) {
13121        // cancel next scheduled automatic transition, if any
13122        this.delay = delay || 0;
13123        this.postback = (postback == undefined) ? true : postback;
13124
13125        clearTimeout(this.interval);
13126
13127        if (this.index < this.length - 1) {
13128            this.slide(this.index + 1, this.speed);
13129        } else {
13130            if (this.postback !== false) {
13131                this.slide(0, this.speed); //if last slide return to start
13132            }
13133        }
13134    },
13135
13136    moveTab: function(iPage, delay, oTarget) {
13137        // control current tab action
13138        // 모바일 상품상세에서 slide영역을 다시 원복함
13139        this.index = iPage;
13140        this.delay = delay || 0;
13141
13142        clearTimeout(this.interval);
13143
13144        this.slide(this.index, this.speed);
13145
13146        if (typeof CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE !== 'undefined') {
13147            CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE.setSwipeImage('', true, iPage, oTarget);
13148        }
13149    },
13150
13151    begin: function() {
13152        var _this = this;
13153
13154        this.interval = (this.delay) ? setTimeout(function() {
13155            _this.next(_this.delay);
13156        }, this.delay) : 0;
13157    },
13158
13159    stop: function() {
13160      this.delay = 0;
13161
13162      clearTimeout(this.interval);
13163    },
13164
13165    resume: function() {
13166      this.delay = this.options.auto || 0;
13167      this.begin();
13168    },
13169
13170    setLength: function(expand) {
13171        this.length = expand;
13172    },
13173
13174    handleEvent: function(e) {
13175        switch (e.type) {
13176            case 'touchstart':
13177                this.onTouchStart(e);
13178                break;
13179            case 'touchmove':
13180                this.onTouchMove(e);
13181                break;
13182            case 'touchcancel':
13183            case 'touchend':
13184                this.onTouchEnd(e);
13185                break;
13186            case 'webkitTransitionEnd':
13187            case 'msTransitionEnd':
13188            case 'oTransitionEnd':
13189            case 'transitionend':
13190                this.transitionEnd(e);
13191                break;
13192            case 'resize':
13193                this.setup();
13194                break;
13195        }
13196    },
13197
13198    transitionEnd: function(e) {
13199      if (this.delay) {
13200          this.begin();
13201      }
13202
13203      this.callback(e, this.index, this.slides[this.index], this);
13204    },
13205
13206    onTouchStart: function(e) {
13207        this.start = {
13208          // get touch coordinates for delta calculations in onTouchMove
13209          pageX: e.touches[0].pageX,
13210          pageY: e.touches[0].pageY,
13211
13212          // set initial timestamp of touch sequence
13213          time: Number(new Date())
13214        };
13215
13216        // used for testing first onTouchMove event
13217        this.isScrolling = undefined;
13218
13219        // reset deltaX
13220        this.deltaX = 0;
13221
13222        // set transition time to 0 for 1-to-1 touch movement
13223        this.element.style.MozTransitionDuration = this.element.style.webkitTransitionDuration = 0;
13224
13225        e.stopPropagation();
13226    },
13227
13228    onTouchMove: function(e) {
13229        // ensure swiping with one touch and not pinching
13230        if (e.touches.length > 1 || e.scale && e.scale !== 1) {
13231            return;
13232        }
13233
13234        this.deltaX = e.touches[0].pageX - this.start.pageX;
13235
13236        // determine if scrolling test has run - one time test
13237        if (typeof this.isScrolling === 'undefined') {
13238            this.isScrolling = !! (this.isScrolling || Math.abs(this.deltaX) < Math.abs(e.touches[0].pageY - this.start.pageY));
13239        }
13240
13241        // if user is not trying to scroll vertically
13242        if (! this.isScrolling) {
13243            // prevent native scrolling
13244            e.preventDefault();
13245
13246            // cancel slideshow
13247            clearTimeout(this.interval);
13248
13249            // increase resistance if first or last slide
13250            this.deltaX =
13251            this.deltaX /
13252            ((! this.index && this.deltaX > 0 // if first slide and sliding left
13253            || this.index == this.length - 1 // or if last slide and sliding right
13254            && this.deltaX < 0 // and if sliding at all
13255            ) ?
13256            (Math.abs(this.deltaX) / this.width + 1) // determine resistance level
13257            : 1); // no resistance if false
13258
13259            // translate immediately 1-to-1
13260            this.element.style.MozTransform = this.element.style.webkitTransform = 'translate3d(' + (this.deltaX - this.index * this.width) + 'px,0,0)';
13261
13262            e.stopPropagation();
13263        }
13264    },
13265
13266    onTouchEnd: function(e) {
13267        // determine if slide attempt triggers next/prev slide
13268        var isValidSlide =
13269            Number(new Date()) - this.start.time < 250 // if slide duration is less than 250ms
13270            && Math.abs(this.deltaX) > 20 // and if slide amt is greater than 20px
13271            || Math.abs(this.deltaX) > this.width/2, // or if slide amt is greater than half the width
13272
13273        // determine if slide attempt is past start and end
13274        isPastBounds =
13275            ! this.index && this.deltaX > 0 // if first slide and slide amt is greater than 0
13276            || this.index == this.length - 1 && this.deltaX < 0; // or if last slide and slide amt is less than 0
13277
13278        // if not scrolling vertically
13279        if (! this.isScrolling) {
13280            // call slide function with slide end value based on isValidSlide and isPastBounds tests
13281            this.slide(this.index + (isValidSlide && ! isPastBounds ? (this.deltaX < 0 ? 1 : -1) : 0), this.speed);
13282        }
13283
13284        e.stopPropagation();
13285    }
13286};
13287
13288/**
13289 * 모바일 상품 더보기 모듈
13290 * @package app/Mobile
13291 * @subpackage Front/Disp/Product
13292 * @version 2.2
13293 *
13294 *
13295 * version 2.2 변경사항
13296 * 1. cache = yes 설정으로 더보기 리스트 유지
13297 * 2. 사용자 html 수정 유무에 상관없이 리스팅
13298 * 3. api에 $review_cnt 추가
13299 */
13300var $M = {
13301    /*
13302     * current module name
13303     */
13304    sModule: 'xans-product-listnormal',
13305    /*
13306     * current module name
13307     */
13308    sMore: 'xans-product-listmore',
13309
13310    /*
13311     * 더보기 버튼에 대한 중복 실행을 막기 위한 flag
13312     */
13313    bLoading: false,
13314
13315    /*
13316     * 모듈별로 object 값
13317     */
13318    oModuleLoading: {},
13319
13320    /*
13321     * init
13322     */
13323    init: function() {
13324        if (this.sModule == 'xans-product-listnormal') {
13325            // 일반상품에 대해 더보기 기능 적용시 페이징 모듈 자동 삭제
13326            EC$('.xans-product-normalpaging').remove();
13327        }
13328    },
13329    /*
13330     * show more
13331     * @param int iActive 모듈 key
13332     * @param int iDisplayGroup 추천/신상품 분류
13333     * @param int iCategoryNo 카테고리 번호
13334     * @param int iCount 주석변수 상품 수
13335     * @param int iSortMethod 정렬방법
13336     * @param string sSupplierCode 공급사코드
13337     * @param bool bInitMore 더보기 기능 초기화 여부
13338     * @param string 품절상품 표시 여부
13339     */
13340    displayMore: function(iActive, iDisplayGroup, iCategoryNo, iCount, iSortMethod, bCache, sSupplierCode, bInitMore, sSoldoutDisplay) {
13341
13342        if (this.oModuleLoading[iDisplayGroup] === true) {
13343            // 로딩 중에는 실행 안함
13344            return;
13345        }
13346
13347        var EC_MORE = (function() {
13348            var sTargetModuleName,
13349                sLiTemplate,
13350                sFirstLiTemplate,
13351                $moreButton,
13352                $currentPageText,
13353                sCurrentPageCookieName,
13354                iRequestPageNum;
13355
13356            /**
13357             * 추가될 상품 정보가 append될 모듈명
13358             * @returns {string}
13359             */
13360            function getTargetModuleName()
13361            {
13362                if (iActive > 0) {
13363                    return $M.sModule + '-' + iActive;
13364                } else {
13365                    return $M.sModule;
13366                }
13367            }
13368            /**
13369             * LI 템플릿을 리턴합니다.
13370             * @returns {string}
13371             */
13372            function getLiTemplate(sPos)
13373            {
13374                var sModuleHtmlForVDOM = CAFE24.FRONT_XANS_TEMPLATE.getTemplateForVDOM(sTargetModuleName);
13375                var oLiObejct = EC$(sModuleHtmlForVDOM).find('ul').first().children('li');
13376                // 재고플러스 사용시 재고현황 API 로드여부 attribute값 삭제
13377                oLiObejct.find('[module="ec-product-wms-stock-manage"]').removeAttr('hasLoaded');
13378                var $li = EC$.fn[sPos].apply(oLiObejct);
13379                var sLiHtmlForVDOM = EC$('<ul>').append($li).html();
13380
13381                return CAFE24.FRONT_XANS_TEMPLATE.convertVDomHtmlToHTML(sLiHtmlForVDOM);
13382            }
13383            /**
13384             * "더보기" 버튼 모듈
13385             * @returns {jQuery}
13386             */
13387            function getMoreButtonElement()
13388            {
13389                if (iActive > 0) {
13390                    return EC$('.' + $M.sMore + '-' + iActive);
13391                } else {
13392                    return EC$('.' + $M.sMore);
13393                }
13394            }
13395            /**
13396             * "현재페이지 표시" 영역
13397             * @returns {jQuery}
13398             */
13399            function getCurrentPageTextElement()
13400            {
13401                if (iDisplayGroup > 1) {
13402                    return EC$('#more_current_page_' + iDisplayGroup);
13403                } else {
13404                    return EC$('#more_current_page');
13405                }
13406            }
13407            /**
13408             * "캐쉬된 현재페이지" 쿠키명
13409             * @returns {string}
13410             */
13411            function getCachedCurrentPageCookieName()
13412            {
13413                var aCookieName = ['mobile_more_current_page'];
13414                if (iCategoryNo > 0) {
13415                    aCookieName.push(iCategoryNo);
13416                }
13417                if (iDisplayGroup > 1) {
13418                    aCookieName.push(iDisplayGroup);
13419                }
13420                return aCookieName.join('_');
13421            }
13422            /**
13423             * 요청할 페이지 번호를 구하여 리턴합니다.
13424             * @return int
13425             *      "더보기 유지 기능 사용" + "더보기 초기화"인 경우 현재 쿠키에 저장된 페이지 번호
13426             *      그 외에는 다음에 가져올 페이지 번호
13427             */
13428            function getRequestPageNum()
13429            {
13430                if (bCache === true && bInitMore === true) {
13431                    // "더보기 유지 기능 사용" + "더보기 초기화"인 경우
13432                    var sCookieCurrentPage = EC$.cookie(sCurrentPageCookieName);
13433                    if (isResetPage() === true) {
13434                        EC$.cookie(sCurrentPageCookieName, 1, -1);
13435                        $currentPageText.text(1);
13436                        $moreButton.data('current_page', 1);
13437                        return 1;
13438                    }
13439
13440                    if (sCookieCurrentPage) {
13441                        return parseInt(sCookieCurrentPage, 10);
13442                    } else {
13443                        return 1;
13444                    }
13445
13446                } else {
13447                    // 그 외
13448                    var iCurrentPage = $moreButton.data('current_page');
13449
13450                    if (iCurrentPage === undefined) {
13451                        iCurrentPage = 1;
13452                    }
13453
13454                    return iCurrentPage + 1;
13455                }
13456            }
13457            /**
13458             * 다음 페이지 상품 정보를 가져올 수 있는 ajax URL을 리턴합니다.
13459             * @returns string
13460             */
13461            function getAjaxUrl()
13462            {
13463
13464                var aParam = {
13465                    cate_no: iCategoryNo,
13466                    display_group: iDisplayGroup,
13467                    supplier_code: sSupplierCode,
13468                    sort_method: iSortMethod,
13469                    page: iRequestPageNum,
13470                    count: iCount,
13471                    bInitMore: (bInitMore === true) ? 'T' : 'F',
13472                    ec_soldout_display: sSoldoutDisplay
13473                };
13474
13475                return CAFE24.MOBILE_UTIL.setAjaxParam(aParam, $M.sModule);
13476            }
13477            /**
13478             * 다음 페이지 상품 정보를 UL Element에 추가해줍니다.
13479             * @param array aData 상품 정보
13480             */
13481            function appendMoreData(aData)
13482            {
13483                EC_MORE.hideMoreButton();
13484
13485                var aHtml = [];
13486                var sTemplate = sLiTemplate;
13487                EC$(aData).each(function(iIndex, aVar) {
13488                    if (iIndex === 0) {
13489                        sTemplate = sFirstLiTemplate;
13490                    } else {
13491                        sTemplate = sLiTemplate;
13492                    }
13493                    aHtml.push(CAFE24.FRONT_XANS_INTERPRETER.fetch(sTemplate, aVar));
13494                });
13495                var sHtml = aHtml.join('');
13496
13497                EC$('.' + sTargetModuleName).each(function() {
13498                    EC$(this).find('ul').first().append(sHtml);
13499                });
13500
13501                // 재고현황 API 로드
13502                if (typeof CAFE24.SHOP_FRONT_NEW_PRODUCT_WMS_STOCK_STATUS !== 'undefined') {
13503                    CAFE24.SHOP_FRONT_NEW_PRODUCT_WMS_STOCK_STATUS.loadStock('ec-product-wms-stock-manage');
13504                }
13505
13506                $currentPageText.text(iRequestPageNum);
13507                $moreButton.data('current_page', iRequestPageNum);
13508
13509                // GE 판매제한: 더보기로 추가된 상품에 판매제한 라벨 재적용 (ECQAUNIT-74655)
13510                // appendMoreData 는 더보기 클릭(ajax)/더보기 유지(storage) 두 경로 공통 진입점
13511                if (typeof CAPP_ASYNC_METHODS !== 'undefined'
13512                    && CAPP_ASYNC_METHODS.hasOwnProperty('GlobaleSellability') === true
13513                    && CAPP_ASYNC_METHODS['GlobaleSellability'].isUse() === true) {
13514                    CAPP_ASYNC_METHODS['GlobaleSellability'].refresh();
13515                }
13516
13517                // GE 통화/환율: 더보기로 추가된 상품 가격을 presentment 로 변환 재적용 (ECQAUNIT-74657)
13518                if (typeof CAPP_ASYNC_METHODS !== 'undefined'
13519                    && CAPP_ASYNC_METHODS.hasOwnProperty('GlobalePrice') === true
13520                    && CAPP_ASYNC_METHODS['GlobalePrice'].isUse() === true) {
13521                    CAPP_ASYNC_METHODS['GlobalePrice'].refresh();
13522                }
13523
13524            }
13525
13526            /**
13527             * 최근 페이지 저장
13528             */
13529            function setCurrentPageCookie()
13530            {
13531                // 캐시 기능 사용이면 쿠키에 현재 페이지 저장
13532                if (bCache === true) {
13533                    EC$.cookie(sCurrentPageCookieName, iRequestPageNum, { expires: 1 });
13534                }
13535            }
13536
13537            /**
13538             * '더보기' 버튼을 숨김 처리합니다.
13539             */
13540            function hideMoreButton()
13541            {
13542                $moreButton.hide();
13543            }
13544
13545            /**
13546             * '더보기' 버튼을 노출 처리합니다.
13547             */
13548            function showMoreButton()
13549            {
13550                $moreButton.show();
13551            }
13552
13553            /**
13554             * Ajax 요청 여부를 리턴합니다.
13555             * @return bool true이면 ajax 요청, false이면 ajax 요청 안함
13556             */
13557            function isCallAjax()
13558            {
13559                if (bInitMore === true && iRequestPageNum <= 1) {
13560                    // 더보기 유지 기능 동작이고 iRequestPageNum 값이 1이하이면 요청 안함
13561                    return false;
13562                }
13563
13564                return true;
13565            }
13566
13567            function setMoreAction(data)
13568            {
13569                if (data.rtn_data.end === true) {
13570                    EC_MORE.hideMoreButton();
13571                }
13572                else {
13573                    setTimeout(EC_MORE.showMoreButton,300);
13574                }
13575
13576                if (data.is_new_product === true) {
13577                    CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT.setReviewTalkCnt();
13578                }
13579            }
13580
13581            /**
13582             * 페이지 초기화여부 반환
13583             * @returns {boolean}
13584             */
13585            function isResetPage()
13586            {
13587                return getStorageList(iCategoryNo || 1, iDisplayGroup || 1) === null;
13588            }
13589
13590            /**
13591             * 스토리지에 저장된 데이터 반환
13592             * @param {number} iCategoryNo
13593             * @param {number} iDisplayGroup
13594             * @returns {object}
13595             */
13596            function getStorageList(iCategoryNo, iDisplayGroup)
13597            {
13598                var sStorageListName = getStorageKey(iCategoryNo, iDisplayGroup);
13599                var oStorageListData = null;
13600
13601                try {
13602                    oStorageListData = JSON.parse(sessionStorage.getItem(sStorageListName));
13603                } catch (e) {
13604                    return null;
13605                }
13606
13607                if (oStorageListData === null) {
13608                    return null;
13609                }
13610
13611                if (isValidStorage(oStorageListData) === true) {
13612                    sessionStorage.removeItem(sStorageListName);
13613                    return null;
13614                }
13615
13616                return oStorageListData;
13617            }
13618
13619            /**
13620             * 스토리지 유효성 검사 (시간만료, 정렬순위 변경, 동일모듈이지만 다른 카운트를 사용하는경우)
13621             * @param {object} oStorageListData
13622             * @returns {boolean}
13623             */
13624            function isValidStorage(oStorageListData)
13625            {
13626                var iNowTime = Math.floor(new Date().getTime() / 1000);
13627                var iSessionTime = 60 * 5;
13628
13629                return parseInt(oStorageListData['iStorageTime']) + iSessionTime < iNowTime ||
13630                    oStorageListData['iSortMethod'] !== iSortMethod ||
13631                    oStorageListData['iCount'] !== iCount;
13632            }
13633
13634            /**
13635             * 다른모듈 데이터를 삭제.
13636             * @param {number} iCategoryNo
13637             * @param {number} iDisplayGroup
13638             * @returns {void}
13639             */
13640            function clearOtherStorage(iCategoryNo, iDisplayGroup)
13641            {
13642                var sStorageListName = getStorageKey(iCategoryNo, iDisplayGroup);
13643
13644                // 현재 모듈 데이터 외 모듈 데이터 삭제
13645                for (var i = 0; i < sessionStorage.length; i++) {
13646                    var sKey = sessionStorage.key(i);
13647                    if (sKey !== sStorageListName && sKey.indexOf(getStoragePrefixKey()) === 0) {
13648                        sessionStorage.removeItem(sKey);
13649                    }
13650                }
13651            }
13652
13653            /**
13654             * 스토리지 키 반환
13655             * @param {number} iCategoryNo
13656             * @param {number} iDisplayGroup
13657             * @returns {string}
13658             */
13659            function getStorageKey(iCategoryNo, iDisplayGroup)
13660            {
13661                return getStoragePrefixKey() + '_' + iCategoryNo + '_' + iDisplayGroup;
13662            }
13663
13664            /**
13665             * 스토리지 키 접두사 반환
13666             * @returns {string}
13667             */
13668            function getStoragePrefixKey()
13669            {
13670                return 'mobile_more_' + ($M.sModule === 'xans-product-listnormal' ? 'listnormal' : 'listmain');
13671            }
13672
13673            sTargetModuleName = getTargetModuleName();
13674            sFirstLiTemplate = getLiTemplate('first');
13675            sLiTemplate = getLiTemplate('last');
13676            $moreButton = getMoreButtonElement();
13677            $currentPageText = getCurrentPageTextElement();
13678            sCurrentPageCookieName = getCachedCurrentPageCookieName();
13679            iRequestPageNum = getRequestPageNum();
13680
13681            return {
13682                isCallAjax: isCallAjax,
13683                getAjaxUrl: getAjaxUrl,
13684                appendMoreData: appendMoreData,
13685                hideMoreButton: hideMoreButton,
13686                showMoreButton: showMoreButton,
13687                setMoreAction: setMoreAction,
13688                getStorageList: getStorageList,
13689                getStorageKey: getStorageKey,
13690                setCurrentPageCookie: setCurrentPageCookie,
13691                clearOtherStorage: clearOtherStorage
13692            };
13693        })();
13694
13695        // ajax
13696        if (EC_MORE.isCallAjax() === true) {
13697
13698            var aParamData = {};
13699            if (EC$('#ec-product-searchdata-form').length > 0) {
13700
13701                EC$('#ec-product-searchdata-catenum').val(iCategoryNo);
13702                CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchPriceData();
13703
13704                EC$('#ec-product-searchdata-form .ec-product-searchdata-form:checked').each(function(idx) {
13705                    var sValues = decodeURIComponent(EC$(this).val());
13706                    if (EC$(this).val() !== sValues) {
13707                        EC$(this).val(encodeURIComponent(sValues));
13708                    } else {
13709                        EC$(this).val(encodeURIComponent(EC$(this).val()));
13710                    }
13711                });
13712
13713                EC$('#ec-product-searchdata-form .ec-product-searchdata-form.ec_search_selected').each(function() {
13714                    if (EC$(this).prop('type') !== 'checkbox') {
13715                        EC$('<input>').attr({type: 'hidden',name: 'search_form[option_data][]',value: encodeURIComponent(EC$(this).attr('sValue'))}).appendTo('#ec-product-searchdata-form');
13716                    }
13717                });
13718
13719                aParamData = EC$('#ec-product-searchdata-form').serialize();
13720
13721
13722            }
13723
13724            var iGetCategory = iCategoryNo;
13725            var iGetDisplay = iDisplayGroup;
13726
13727            if (iGetCategory === 0) {
13728                iGetCategory = 1;
13729            }
13730
13731            if (iGetDisplay === 0) {
13732                iGetDisplay = 1;
13733            }
13734
13735            var oStorageListData = EC_MORE.getStorageList(iGetCategory, iGetDisplay);
13736
13737            // 현재 시간 Unix Timstamp
13738            var iNowTime = Math.floor(new Date().getTime() / 1000);
13739
13740            if (bInitMore === true && oStorageListData !== null) {
13741                // 더보기를 한번이라도 눌렀고, 스토리지 데이터가 반환된다면 스토리지 데이터로 사용
13742                EC_MORE.appendMoreData(oStorageListData.rtn_data.data);
13743                EC_MORE.setMoreAction(oStorageListData);
13744                EC_MORE.setCurrentPageCookie();
13745                return;
13746            }
13747
13748            EC$.ajax({
13749                type: 'get',
13750                url: EC_MORE.getAjaxUrl(),
13751                data: aParamData,
13752                dataType: 'json',
13753                success: function(data) {
13754                    if (data.rtn_code === '1000') {
13755                        EC_MORE.appendMoreData(data.rtn_data.data);
13756                        EC_MORE.setMoreAction(data);
13757
13758                        if (bInitMore === false) {
13759                            data.iStorageTime = iNowTime;
13760                            data.iSortMethod = iSortMethod;
13761                            data.iCount = iCount;
13762
13763                            if (oStorageListData !== null) {
13764                                // 초기 구동이 아니면서 세션 스토리지에 데이터가 있는 경우에는 append
13765                                data.rtn_data.data = oStorageListData.rtn_data.data.concat(data.rtn_data.data);
13766                            }
13767                        }
13768
13769                        // 최종 생성된 데이터 세션 스토리지에 저장
13770                        try {
13771                            EC_MORE.clearOtherStorage(iGetCategory, iGetDisplay);
13772                            sessionStorage.setItem(EC_MORE.getStorageKey(iGetCategory, iGetDisplay), JSON.stringify(data));
13773                            // 세션스토리지 용량이 초과되지않은경우에만 최근본페이지 갱신.
13774                            EC_MORE.setCurrentPageCookie();
13775                            ReferenceCurrencyPrice.init();
13776                        } catch (e) {
13777                        }
13778                        if (CAPP_ASYNC_METHODS.hasOwnProperty('Soldouticon') === true && CAPP_ASYNC_METHODS["Soldouticon"].isUse() === true) {
13779                            CAPP_ASYNC_METHODS["Soldouticon"].execute();
13780                        }
13781                    } else {
13782                        alert('상품을 추가로 더 불러오는 과정에 문제가 발생했습니다. 지속적으로 발생할 경우 운영자에게 문의하세요.');
13783
13784                        EC_MORE.hideMoreButton();
13785
13786                        return false;
13787                    }
13788                },
13789                error: function (xhr, status, error) {
13790                    return false;
13791                },
13792                beforeSend: function () {
13793                    $M.oModuleLoading[iDisplayGroup] = true;
13794                },
13795                complete: function () {
13796                    $M.oModuleLoading[iDisplayGroup] = false;
13797                }
13798            });
13799        }
13800    } ,
13801    setDisplayPageMore: function(iActive, iDisplayGroup, iCategoryNo, iCount, iSortMethod, bCache, sSupplierCode, bInitMore, sSoldoutDisplay) {
13802        this.displayMore(iActive, iDisplayGroup, iCategoryNo, iCount, iSortMethod, bCache, sSupplierCode, true, sSoldoutDisplay);
13803    }
13804};
13805
13806/**
13807 * 모바일 상품 스와이프 모듈
13808 * @package app/Shop
13809 * @subpackage Front/Disp/Product
13810 * @since 2014. 2. 12.
13811 * @update 2014. 5. 29.
13812 * @version 2.2
13813 *
13814 * 2.2 개선사항
13815 * 1. 데이터 ajax 로딩 추가
13816 * 2. multi, single 형태 추가
13817 */
13818var $S = {
13819    /*
13820     * current module name
13821     */
13822    sModule: 'xans-product-listmain',
13823
13824    /*
13825     * swipe action name
13826     */
13827    sModuleSwipe: '',
13828
13829    /*
13830     * mode
13831     */
13832    sMode: 'multi',
13833
13834    /*
13835     * slider
13836     */
13837    bSlider: false,
13838
13839    /*
13840     * swipeable
13841     */
13842    sSwipeable: 'yes',
13843
13844    /*
13845     * sParam
13846     */
13847    sParam: '',
13848
13849    /*
13850     * grid
13851     */
13852    sGrid: 'grid3',
13853
13854    /*
13855     * grid array
13856     */
13857    aGrid: {'grid2': 2, 'grid3': 3, 'grid4': 4, 'grid5': 5},
13858
13859    /*
13860     * start slide
13861     */
13862    iStart: 0,
13863
13864    /*
13865     * page
13866     */
13867    iPage: 1,
13868
13869    /*
13870     * page block
13871     */
13872    iPageBlock: 3,
13873
13874    /*
13875     * line
13876     */
13877    iLine: 1,
13878
13879    /*
13880     * limit circle
13881     */
13882    iLimit: 9,
13883
13884    /*
13885     * active div
13886     */
13887    iActive: 0,
13888
13889    /*
13890     * save li element
13891     */
13892    aElement: [],
13893
13894    /*
13895     * save circle element
13896     */
13897    aButton: [],
13898
13899    /*
13900     * product class
13901     */
13902    $product: null,
13903
13904    /*
13905     * product ul
13906     */
13907    $productModule: null,
13908
13909    /*
13910     * product list li
13911     */
13912    $productList: null,
13913
13914    /*
13915     * ajax loading
13916     */
13917    bAjax: true,
13918
13919    /*
13920     * generate dom
13921     */
13922    bGenerate: false,
13923
13924    /*
13925     * ajax count
13926     */
13927    iAjax: 0,
13928
13929    /*
13930     * auto slide interval
13931     */
13932    iAutoSlideInterval: 0,
13933
13934    /*
13935     *  paging ui
13936     */
13937    sPagingType: 'circle',
13938
13939    iProductTotal: 0,
13940
13941    /*
13942     * cache 사용여부
13943     */
13944    sCache: 'no',
13945
13946    /*
13947     * init
13948     */
13949    init: function() {
13950        // set param
13951        this.setParam();
13952
13953         // set obejct
13954        this.setObject();
13955
13956        // set block
13957        this.setBlock();
13958
13959        // validate
13960        if (this.validate() === false) return;
13961
13962         // generate
13963        this.generate();
13964
13965        // load swipe
13966        this.load();
13967    },
13968
13969    /*
13970     * set param
13971     */
13972    setParam: function() {
13973        try
13974        {
13975            this.sModuleSwipe = this.sModule.replace(/-/g, "_");
13976            this.iAjax = oMobileSliderData.iSliderLimit;
13977        }
13978        catch (e) { }
13979    },
13980
13981    /*
13982     * set block
13983     */
13984    setBlock: function() {
13985        // set block num
13986        this.iPageBlock = (this.sMode == 'multi') ? this.iLine * this.aGrid[this.sGrid] : 1;
13987    },
13988
13989    /*
13990     * set obejct
13991     */
13992    setObject: function() {
13993        try
13994        {
13995            // current module class
13996            this.sActiveProduct = this.iActive > 0 ? this.sModule + '-'+this.iActive : this.sModule;
13997
13998            // div
13999            this.$product = $('.' + this.sActiveProduct);
14000
14001            // div > ul
14002            this.$productModule = this.$product.find('ul').first();
14003            this.$productModule.css('webkit-backface-visibility', 'hidden');
14004
14005            // div > ul > li > ul > li
14006            this.$productList = this.$productModule.find('>li');
14007        }
14008        catch (e) { }
14009    },
14010
14011    /*
14012     * validate
14013     */
14014    validate: function() {
14015        // not use swipe
14016        if (this.sSwipeable != 'yes') return false;
14017
14018        // empty ul
14019        if (this.$productModule.length < 1) return false;
14020
14021        // empty li
14022        if (this.$productList.length < 1) return false;
14023
14024        // mobilemaincategory-slider exception
14025        if (this.$productModule.find('.afterNone').length > 0) return true;
14026
14027        // no condition swipe
14028        if (this.$productList.length <= this.iPageBlock) return false;
14029    },
14030
14031    /*
14032     * ganerate swipe single dom
14033     */
14034    generate: function() {
14035        if (this.sMode == 'single') { this.generateSingle(); }
14036        else { this.generateMulti(); }
14037    },
14038
14039    /*
14040     * prepare for element
14041     */
14042    prepare: function() {
14043        var $prepare = {
14044            /*
14045             * reset element and circle
14046             */
14047            reset: function() {
14048                $S.aElement = [];
14049                $S.aButton = [];
14050            },
14051
14052            /*
14053             * set target id
14054             */
14055            setId: function() {
14056                $S.$product.attr('id', $S.sModule + '-slider-' + $S.iActive);
14057            }
14058        };
14059        $prepare.reset();
14060        $prepare.setId();
14061    },
14062
14063    /*
14064     * ganerate swipe single dom
14065     */
14066    generateSingle: function() {
14067        // prepare
14068        this.prepare();
14069
14070        // make li > ul > li
14071        for (var i=0; i<this.$productList.length; i++) {
14072            this.makeButton(i);
14073        }
14074
14075        // call pagenate
14076        this.makePagenate();
14077    },
14078
14079
14080    /*
14081     * reset grid
14082     */
14083    resetGrid: function() {
14084        for (var sKey in this.aGrid) {
14085            if (this.$productModule.hasClass(sKey) === true) { this.$productModule.empty().removeClass(sKey); }
14086        }
14087    },
14088
14089    /*
14090     * ganerate swipe multi dom
14091     */
14092    generateMulti: function() {
14093        // prepare
14094        this.prepare();
14095
14096        if (this.bGenerate === false) return;
14097
14098        // save li
14099        this.$productList.each(function() { $S.aElement.push($(this).clone(true)); });
14100
14101        // delete li and grid2, gird3, grid4
14102        this.resetGrid();
14103
14104        this.iProductTotal = this.aElement.length;
14105        this.iTotalPage = Math.ceil(this.iProductTotal / this.iPageBlock);
14106
14107        // make li > ul > li
14108        for (var i=0, k=1, j=0; i<this.iProductTotal; i++, k++)
14109        {
14110            // templete for li > ul
14111            var $template = (j == 0) ? $("<li>", { html: $("<ul>", {'class': this.sGrid}) }) : $('<li>', { html: $("<ul>", {'class': this.sGrid}), css: {'display': 'none'} });
14112
14113            // add li > ul
14114            if (k == 1)
14115            {
14116                this.$productModule.append($template);
14117                // <  현재페이지 / 총페이지 >
14118                if (this.sPagingType !== 'number') {
14119                    this.makeButton(j);
14120                }
14121            }
14122
14123            // add li > ul > li
14124            this.$product.children('ul').children('li').eq(j).children('ul').append(this.aElement[i]);
14125
14126            // see block
14127            if (k == this.iPageBlock)
14128            {
14129                k = 0;
14130                j++;
14131            }
14132        }
14133
14134        if (this.sPagingType === 'number') {
14135            this.makeNumber();
14136        }
14137        // not necessary pagenate
14138        if (i < (parseInt(this.iPageBlock) + 1)) return;
14139
14140        // call pagenate
14141        this.makePagenate();
14142    },
14143
14144    makeButton: function(iCnt) {
14145        // ECQAINT-14112 롤링 및 넘버링 타입은 '모바일 환경설정'의 기본 사양에 따라 최대 갯수 5개로 설정
14146        if (this.sPagingType == 'rolling' || this.sPagingType == 'numbering') {
14147            this.iLimit = 5;
14148        } else if (this.sPagingType !== 'circle') {
14149            this.iLimit = 4;
14150        }
14151
14152        var iNum = iCnt + 1,
14153        sSelected = (iCnt == 0) ? 'selected' : '',
14154        iPage = Math.ceil(iNum / this.iLimit),
14155        sLimitStyle = (iNum > this.iLimit) ? 'style="display:none"' : '',
14156        sName = this.sActiveProduct + '_page_'+iPage+'_'+iNum;
14157
14158        if (this.sPagingType === 'fix') {
14159            var sSelected = (iCnt == 0) ? 'this' : 'other';
14160            this.divPaginateName = 'typeList';
14161            this.aButton.push('<li name="'+sName+'" ' + sLimitStyle +'><a class="' + sSelected + '" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.moveTab(' + iCnt + ', ' + this.iAutoSlideInterval +');return false;">' + iNum +'</a></li>');
14162        } else if (this.sPagingType === 'numbering') {
14163            this.divPaginateName = 'typeNumber';
14164        } else if (this.sPagingType === 'rolling') {
14165            this.divPaginateName = 'typeRoll';
14166            this.aButton.push('<li class="' + sSelected + '" name="' + sName + '"><a href="#none" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.moveTab(' + iCnt + ', ' + this.iAutoSlideInterval +');return false;">' + iNum + '</a></li>');
14167        } else {
14168            // circle
14169            this.divPaginateName = 'typeSwipe';
14170            this.aButton.push('<button name="'+sName+'" type="button" ' + sLimitStyle +' class="circle  ' + sSelected + '" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.moveTab(' + iCnt + ', ' + this.iAutoSlideInterval +', $(this));return false;"><span>' + iNum +'번째 리스트</span></button>');
14171        }
14172    },
14173
14174    /*
14175     * make fix number
14176     */
14177    makeNumber: function() {
14178        this.divPaginateName = 'typeTotal';
14179        sName = this.sModule+'-'+this.iActive+'_page';
14180        this.aButton.push('<span name="' + sName + '" class="page"><strong>1</strong> / <span>' + this.iTotalPage + '</span></span>');
14181    },
14182
14183    /*
14184     * make pagenation
14185     */
14186    makePagenate: function() {
14187        var sSwipeId = this.sModule + '-swipe-button-' + this.iActive;
14188        var sPaginateStyle = '';
14189        var aBtn = [];
14190        if (this.sPagingType === 'fix') {
14191            aBtn.push('<p class="prev" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.prev();return false;"><a href="#none"><span>이전 페이지</span></a></p>');
14192            aBtn.push('<ol id='+ sSwipeId +'>'+this.aButton.join('')+'</ol>');
14193            aBtn.push('<p class="next" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.next();return false;"><a href="#none"><span>다음 페이지</span></a></p>');
14194        } else if (this.sPagingType === 'numbering') {
14195            aBtn.push('<p><strong>1</strong> / <span>' + this.$productList.length + '</span></p>');
14196        } else if (this.sPagingType === 'rolling') {
14197            sPaginateStyle = 'position:static;';
14198            aBtn.push('<ol id='+ sSwipeId +' class="grid' + this.$productList.length + '">'+this.aButton.join('')+'</ol>');
14199        } else if (this.sPagingType === 'number') {
14200            aBtn.push('<p class="prev" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.prev();return false;"><a href="#none"><span>이전 페이지</span></a></p>');
14201            aBtn.push(this.aButton.join(''));
14202            aBtn.push('<p class="next" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.next();return false;"><a href="#none"><span>다음 페이지</span></a></p>');
14203        } else {
14204            aBtn.push('<button type="button" class="prev" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.prev();return false;"><span>이전 리스트</span></button>');
14205            aBtn.push('<span id="' + sSwipeId + '">' + this.aButton.join('') +'</span>');
14206            aBtn.push('<button type="button" class="next" onclick="$' + this.sModuleSwipe + '_slider_' + this.iActive + '.next();return false;"><span>다음 리스트</span></button>');
14207        }
14208
14209        var sPaginateForm = '<div class="paginate ec-base-paginate '+this.divPaginateName+'" style="' + sPaginateStyle + '">' + aBtn.join('') + '</div>';
14210
14211        if ($S.bSlider === false) { this.$product.append(sPaginateForm); }
14212    },
14213
14214    /*
14215     * call ajax
14216     */
14217    callAjax: function() {
14218        this.iPage++;
14219        this.setAjaxParam();
14220
14221        var $ajaxLoadContainer = {
14222            load: function() {
14223                var $load = '<div class="loading"><img src="//img.echosting.cafe24.com/design/skin/mobile/img_loading.gif" alt="" /></div>',
14224                    $dimmed = '<div class="dimmed"></div>';
14225                $('body').append($load + $dimmed);
14226            },
14227            remove: function() {
14228                $('body').find('div.loading, div.dimmed').remove();
14229            }
14230        };
14231
14232        $.ajax({
14233            type: "get",
14234            url: this.sParam,
14235            dataType: "json",
14236            success: function(data) {
14237                try {
14238                    if (data.rtn_code == '1000') {
14239                        $(data.rtn_data.data).each(function(index, node) {
14240                            // new index
14241                            var newElementIndex = $S.iAjax + index;
14242                            // append new element
14243                            $S.$productModule.append(CAFE24.MOBILE_UTIL.convertNode(node));
14244                            // set onerror img
14245                            CAFE24.MOBILE_UTIL.setDefaultImage();
14246                            // element setting
14247                            $S.$product.find('li').eq(newElementIndex).css({
14248                                width: $(window).width() + 'px',
14249                                display: 'table-cell',
14250                                'vertical-align': 'top'
14251                            }).bind('click', function() { globalPictorlControl($S.$product); });
14252                        });
14253
14254                        var len = data.rtn_data.data.length, currentSlider = '$' + $S.sModuleSwipe + '_slider_' + $S.iActive;
14255
14256                        // set swipe scale
14257                        $S.$productModule.width($S.$productModule.width() + ($(window).width() * len));
14258                        // add ajax condition
14259                        $S.iAjax += len;
14260                        // stop ajax control
14261                        if (len < oMobileSliderData.iSliderLimit) { $S.bAjax = false; }
14262                    } else {
14263                        alert('상품을 추가로 더 불러오는 과정에 문제가 발생했습니다. 지속적으로 발생할 경우 운영자에게 문의하세요.');
14264                        return false;
14265                    }
14266                } catch (e) {
14267                    $ajaxLoadContainer.remove();
14268                }
14269            },
14270            error: function(xhr,status,error) {
14271                //alert('네크워크나 상품API연동에 문제가 있습니다. 지속적으로 발생할 경우 운영자에게 문의하세요.');
14272                return false;
14273            },
14274            beforeSend: function() {
14275                $ajaxLoadContainer.load();
14276            },
14277            complete: function() {
14278                $ajaxLoadContainer.remove();
14279                // set swipe module length && excute next slider
14280                var currentSlider = '$' + $S.sModuleSwipe + '_slider_' + $S.iActive;
14281                eval(currentSlider + '.setLength(' + $S.iAjax + ');');
14282            }
14283        });
14284    },
14285
14286    /*
14287     * set Param
14288     */
14289    setAjaxParam: function() {
14290        var aParam = [];
14291
14292        aParam['cate_no'] = oMobileSliderData.iCategoryNo;
14293        aParam['page'] = this.iPage;
14294        aParam['count'] = oMobileSliderData.iSliderLimit;
14295
14296        this.sParam = CAFE24.MOBILE_UTIL.setAjaxParam(aParam, this.sModule);
14297    },
14298
14299    /*
14300     * load swipe js
14301     */
14302    load: function() {
14303        try
14304        {
14305            var aSwipeVars = [],
14306                $swipe = document.getElementById('' + this.sModule + '-slider-' + this.iActive + ''),
14307                $now = this.$product.find('div.swipePage').find('span.now');
14308
14309            if (this.sPagingType !== 'circle') {
14310                this.$product.find('.typeSwipe .prev, .typeSwipe .next').show();
14311            }
14312
14313            // 상품의 고유한 값(상품 번호 및 카테고리 번호 등)을 지정
14314            // 이렇게 하지 않으면 스와이프 모듈을 하나로 인식하여 처리되기 때문
14315            var sProductInfo = '';
14316
14317            // try-catch로 모듈의 상품 정보를 불러와서 처리하고, 정보가 없으면 처리하지 않음
14318            try {
14319                sProductInfo = this.$productModule.find('li').first().attr('data-param').replace(/\?/gi, '').replace(/\&\=/gi, '_');
14320            } catch (e) {
14321            }
14322
14323            aSwipeVars.push('$' + this.sModuleSwipe + '_slider_' + this.iActive + ' = new SwipeClient($swipe, {');
14324            aSwipeVars.push('    startSlide: ' + this.iStart + ',');
14325            aSwipeVars.push('    auto: ' + this.iAutoSlideInterval + ',');
14326            aSwipeVars.push('    cache: \'' + this.sCache + '\',');
14327            aSwipeVars.push('    elementId: \'' + this.sModuleSwipe + '_slider_' + sProductInfo + '\',');
14328            aSwipeVars.push('    callback: function(e, pos, ele, obj) {');
14329            aSwipeVars.push('        if (obj.container.id == "xans-layout-mobilemaincategory-slider-0") { globalCategorySetUi(mode = "init", pos); }');
14330            aSwipeVars.push('        try { if (globalPictorialLoad === true) { globalPictorialSetUi($S.$product, pos) } } catch(e) {}');
14331            aSwipeVars.push('        var iSelectedPos = pos + 1;');
14332            aSwipeVars.push('        if ($S.bSlider === true) { $now.text(iSelectedPos); }');
14333            aSwipeVars.push(this.getSwipeButtonDisplay());
14334            aSwipeVars.push('    }');
14335            aSwipeVars.push('});');
14336
14337            eval(aSwipeVars.join(''));
14338        }
14339        catch (e) { }
14340    },
14341
14342    getSwipeButtonDisplay: function() {
14343        var sSelected = 'selected';
14344        var sChildSelector = '';
14345        if (this.sPagingType === 'fix') {
14346            sSelected = 'this';
14347            sChildSelector = ' > a';
14348        }
14349
14350        var sSwipeVars = '';
14351        if (this.sPagingType === 'number') {
14352            sSwipeVars = '        $("[name^=\'' + this.sActiveProduct + '_page\'] > strong").text(iSelectedPos);';
14353        } else if (this.sPagingType === 'numbering') {
14354            sSwipeVars = '$(".' + this.divPaginateName + ' > p > strong").text(iSelectedPos);';
14355        } else if (this.sPagingType === 'rolling') {
14356            sSwipeVars = '         $("[name^=\'' + this.sActiveProduct + '_page\']").removeClass(\'' + sSelected + '\');';
14357            sSwipeVars += '        $("[name=\'' + this.sActiveProduct + '_page_1_"+iSelectedPos+"\']").addClass(\'' + sSelected + '\');';
14358        } else {
14359            sSwipeVars = '         var iPage = pos === ' + this.iTotalPage + ' ? Math.ceil(' + this.iTotalPage + ' / $S.iLimit) : Math.ceil(iSelectedPos / $S.iLimit);';
14360
14361            /*
14362                ECHOSTING-251668 show/hide 조건 삭제
14363
14364                sSwipeVars += '        if ((pos % $S.iLimit === 0) || (iSelectedPos % $S.iLimit === 0 || iSelectedPos === ' + this.iTotalPage + ')) {';
14365                sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page\']").hide();';
14366                sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page_"+iPage+"\']").show();';
14367                sSwipeVars += '        }';
14368                sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page\']' + sChildSelector + '").removeClass(\'' + sSelected + '\');';
14369                sSwipeVars += '        $("[name=\'' + this.sActiveProduct + '_page_"+iPage+"_"+iSelectedPos+"\']' + sChildSelector + '").addClass(\'' + sSelected + '\');';
14370            */
14371
14372            sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page\']").hide();';
14373            sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page_"+iPage+"\']").show();';
14374            sSwipeVars += '        $("[name^=\'' + this.sActiveProduct + '_page\']' + sChildSelector + '").removeClass(\'' + sSelected + '\');';
14375            sSwipeVars += '        $("[name=\'' + this.sActiveProduct + '_page_"+iPage+"_"+iSelectedPos+"\']' + sChildSelector + '").addClass(\'' + sSelected + '\');';
14376        }
14377        return sSwipeVars;
14378    }
14379
14380};
14381
14382EC$(function() {
14383    sAttribute = 'ec-data-src';
14384    if (EC$('img['+sAttribute+']').length > 0) {
14385        CAFE24.lazyload();
14386    }
14387});
14388
14389/**
14390 * IntersectionObserver와 MutationObserver를 이용한 Lazyload
14391 * @constructor
14392 */
14393CAFE24.lazyload = function() {
14394    var oConfig = {
14395        rootMargin: '0px 0px 50px 0px',
14396        threshold: 0
14397    };
14398
14399    var placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=';
14400    EC$('img['+sAttribute+']').attr('src', placeholder);
14401
14402    /**
14403     * IntersectionObserver에 객체를 등록하는 메소드
14404     * @type {IntersectionObserver|*}
14405     */
14406    var oIntersection = new IntersectionObserver(function (oEntry, self) {
14407        var iEntryLength = oEntry.length;
14408        for (var i = 0; i < iEntryLength; i++) {
14409            if (oEntry[i].target.tagName !== 'IMG') {
14410                continue;
14411            }
14412            if (oEntry[i].target.hasAttribute(sAttribute) === false) {
14413                continue;
14414            }
14415            oEntry[i].target.src = placeholder;
14416            if (oEntry[i].isIntersecting) {
14417                preloadImage(oEntry[i].target);
14418                self.unobserve(oEntry[i].target);
14419            }
14420        }
14421        // 상품목록에 블럭형 레이이아웃이 존재할때만 이미지 로딩 이후 호출
14422        if (typeof resizeContent === 'function') {
14423            resizeContent();
14424        }
14425    }, oConfig);
14426
14427    /**
14428     * MutationObserver에 객체를 등록하는 메소드
14429     * @type {MutationObserver}
14430     */
14431    var oMutation = new MutationObserver(function (oMutationElement) {
14432        var iMutationLength = oMutationElement.length;
14433        for (var i = 0; i < iMutationLength; i++) {
14434            if (oMutationElement[i].addedNodes.length > 0) {
14435                oMutationElement[i].addedNodes.forEach(function (currnetValue) {
14436                    registIntersection(currnetValue);
14437                });
14438            }
14439        }
14440    });
14441
14442    /**
14443     * Mutation에서 검출된 객체를 Intersection으로 등록하는 메소드
14444     * @param oParentElement
14445     */
14446    function registIntersection(oParentElement) {
14447        if (typeof oParentElement.querySelectorAll !== 'function') {
14448            return;
14449        }
14450        var oImage = oParentElement.querySelectorAll('img['+sAttribute+']');
14451        for (var i=0; i < oImage.length; i++) {
14452            oIntersection.observe(oImage[i]);
14453        }
14454    }
14455
14456    /**
14457     * 실제 이미지 교체 메소드
14458     * @param img
14459     */
14460    function preloadImage(img) {
14461        var src = img.getAttribute(sAttribute);
14462        if (!src) return;
14463        img.src = src;
14464        img.removeAttribute(sAttribute);
14465    }
14466
14467    var oNodeList = document.body.childNodes;
14468    var oMutationConfig = {
14469        childList: true,
14470        subtree: true
14471    };
14472    var iNodeListLength = oNodeList.length;
14473    if (iNodeListLength === 0) {
14474        return;
14475    }
14476    for (var i = 0; i < iNodeListLength; i++) {
14477        registIntersection(oNodeList[i]);
14478        oMutation.observe(oNodeList[i], oMutationConfig);
14479    }
14480};
14481
14482var EC_lazyload = CAFE24.lazyload;
14483
14484/*!
14485 * Shim for MutationObserver interface
14486 * Author: Graeme Yeates (github.com/megawac)
14487 * Repository: https://github.com/megawac/MutationObserver.js
14488 * License: WTFPL V2, 2004 (wtfpl.net).
14489 * Though credit and staring the repo will make me feel pretty, you can modify and redistribute as you please.
14490 * Attempts to follow spec (https://www.w3.org/TR/dom/#mutation-observers) as closely as possible for native javascript
14491 * See https://github.com/WebKit/webkit/blob/master/Source/WebCore/dom/MutationObserver.cpp for current webkit source c++ implementation
14492 */
14493
14494/**
14495 * prefix bugs:
14496 - https://bugs.webkit.org/show_bug.cgi?id=85161
14497 - https://bugzilla.mozilla.org/show_bug.cgi?id=749920
14498 * Don't use WebKitMutationObserver as Safari (6.0.5-6.1) use a buggy implementation
14499 */
14500window.MutationObserver = window.MutationObserver || (function(undefined) {
14501        "use strict";
14502        /**
14503         * @param {function(Array.<MutationRecord>, MutationObserver)} listener
14504         * @constructor
14505         */
14506        function MutationObserver(listener) {
14507            /**
14508             * @type {Array.<Object>}
14509             * @private
14510             */
14511            this._watched = [];
14512            /** @private */
14513            this._listener = listener;
14514        }
14515
14516        /**
14517         * Start a recursive timeout function to check all items being observed for mutations
14518         * @type {MutationObserver} observer
14519         * @private
14520         */
14521        function startMutationChecker(observer) {
14522            (function check() {
14523                var mutations = observer.takeRecords();
14524
14525                if (mutations.length) { // fire away
14526                    // calling the listener with context is not spec but currently consistent with FF and WebKit
14527                    observer._listener(mutations, observer);
14528                }
14529                /** @private */
14530                observer._timeout = setTimeout(check, MutationObserver._period);
14531            })();
14532        }
14533
14534        /**
14535         * Period to check for mutations (~32 times/sec)
14536         * @type {number}
14537         * @expose
14538         */
14539        MutationObserver._period = 30;
14540
14541        /**
14542         * Exposed API
14543         * @expose
14544         * @final
14545         */
14546        MutationObserver.prototype = {
14547            /**
14548             * see https://dom.spec.whatwg.org/#dom-mutationobserver-observe
14549             * not going to throw here but going to follow the current spec config sets
14550             * @param {Node|null} $target
14551             * @param {Object|null} config : MutationObserverInit configuration dictionary
14552             * @expose
14553             * @return undefined
14554             */
14555            observe: function($target, config) {
14556                /**
14557                 * Using slightly different names so closure can go ham
vendor: 3,935 bytes, lines 14558-14665
14558                 * @type {!Object} : A custom mutation config
14559                 */
14560                var settings = {
14561                    attr: !! (config.attributes || config.attributeFilter || config.attributeOldValue),
14562
14563                    // some browsers enforce that subtree must be set with childList, attributes or characterData.
14564                    // We don't care as spec doesn't specify this rule.
14565                    kids: !! config.childList,
14566                    descendents: !! config.subtree,
14567                    charData: !! (config.characterData || config.characterDataOldValue)
14568                };
14569
14570                var watched = this._watched;
14571
14572                // remove already observed target element from pool
14573                for (var i = 0; i < watched.length; i++) {
14574                    if (watched[i].tar === $target) watched.splice(i, 1);
14575                }
14576
14577                if (config.attributeFilter) {
14578                    /**
14579                     * converts to a {key: true} dict for faster lookup
14580                     * @type {Object.<String,Boolean>}
14581                     */
14582                    settings.afilter = reduce(config.attributeFilter, function(a, b) {
14583                        a[b] = true;
14584                        return a;
14585                    }, {});
14586                }
14587
14588                watched.push({
14589                    tar: $target,
14590                    fn: createMutationSearcher($target, settings)
14591                });
14592
14593                // reconnect if not connected
14594                if (!this._timeout) {
14595                    startMutationChecker(this);
14596                }
14597            },
14598
14599            /**
14600             * Finds mutations since last check and empties the "record queue" i.e. mutations will only be found once
14601             * @expose
14602             * @return {Array.<MutationRecord>}
14603             */
14604            takeRecords: function() {
14605                var mutations = [];
14606                var watched = this._watched;
14607
14608                for (var i = 0; i < watched.length; i++) {
14609                    watched[i].fn(mutations);
14610                }
14611
14612                return mutations;
14613            },
14614
14615            /**
14616             * @expose
14617             * @return undefined
14618             */
14619            disconnect: function() {
14620                this._watched = []; // clear the stuff being observed
14621                clearTimeout(this._timeout); // ready for garbage collection
14622                /** @private */
14623                this._timeout = null;
14624            }
14625        };
14626
14627        /**
14628         * Simple MutationRecord pseudoclass. No longer exposing as its not fully compliant
14629         * @param {Object} data
14630         * @return {Object} a MutationRecord
14631         */
14632        function MutationRecord(data) {
14633            var settings = { // technically these should be on proto so hasOwnProperty will return false for non explicitly props
14634                type: null,
14635                target: null,
14636                addedNodes: [],
14637                removedNodes: [],
14638                previousSibling: null,
14639                nextSibling: null,
14640                attributeName: null,
14641                attributeNamespace: null,
14642                oldValue: null
14643            };
14644            for (var prop in data) {
14645                if (has(settings, prop) && data[prop] !== undefined) settings[prop] = data[prop];
14646            }
14647            return settings;
14648        }
14649
14650        /**
14651         * Creates a func to find all the mutations
14652         *
14653         * @param {Node} $target
14654         * @param {!Object} config : A custom mutation config
14655         */
14656        function createMutationSearcher($target, config) {
14657            /** type {Elestuct} */
14658            var $oldstate = clone($target, config); // create the cloned datastructure
14659
14660            /**
14661             * consumes array of mutations we can push to
14662             *
14663             * @param {Array.<MutationRecord>} mutations
14664             */
14665            return function
vendor: 16,374 bytes, lines 14665-14993
14665(mutations) {
14666                var olen = mutations.length, dirty;
14667
14668                if (config.charData && $target.nodeType === 3 && $target.nodeValue !== $oldstate.charData) {
14669                    mutations.push(new MutationRecord({
14670                        type: "characterData",
14671                        target: $target,
14672                        oldValue: $oldstate.charData
14673                    }));
14674                }
14675
14676                // Alright we check base level changes in attributes... easy
14677                if (config.attr && $oldstate.attr) {
14678                    findAttributeMutations(mutations, $target, $oldstate.attr, config.afilter);
14679                }
14680
14681                // check childlist or subtree for mutations
14682                if (config.kids || config.descendents) {
14683                    dirty = searchSubtree(mutations, $target, $oldstate, config);
14684                }
14685
14686                // reclone data structure if theres changes
14687                if (dirty || mutations.length !== olen) {
14688                    /** type {Elestuct} */
14689                    $oldstate = clone($target, config);
14690                }
14691            };
14692        }
14693
14694        /* attributes + attributeFilter helpers */
14695
14696        // Check if the environment has the attribute bug (#4) which cause
14697        // element.attributes.style to always be null.
14698        var hasAttributeBug = document.createElement("i");
14699        hasAttributeBug.style.top = 0;
14700        hasAttributeBug = hasAttributeBug.attributes.style.value != "null";
14701
14702        /**
14703         * Gets an attribute value in an environment without attribute bug
14704         *
14705         * @param {Node} el
14706         * @param {Attr} attr
14707         * @return {String} an attribute value
14708         */
14709        function getAttributeSimple(el, attr) {
14710            // There is a potential for a warning to occur here if the attribute is a
14711            // custom attribute in IE<9 with a custom .toString() method. This is
14712            // just a warning and doesn't affect execution (see #21)
14713            return attr.value;
14714        }
14715
14716        /**
14717         * Gets an attribute value with special hack for style attribute (see #4)
14718         *
14719         * @param {Node} el
14720         * @param {Attr} attr
14721         * @return {String} an attribute value
14722         */
14723        function getAttributeWithStyleHack(el, attr) {
14724            // As with getAttributeSimple there is a potential warning for custom attribtues in IE7.
14725            return attr.name !== "style" ? attr.value : el.style.cssText;
14726        }
14727
14728        var getAttributeValue = hasAttributeBug ? getAttributeSimple : getAttributeWithStyleHack;
14729
14730        /**
14731         * fast helper to check to see if attributes object of an element has changed
14732         * doesnt handle the textnode case
14733         *
14734         * @param {Array.<MutationRecord>} mutations
14735         * @param {Node} $target
14736         * @param {Object.<string, string>} $oldstate : Custom attribute clone data structure from clone
14737         * @param {Object} filter
14738         */
14739        function findAttributeMutations(mutations, $target, $oldstate, filter) {
14740            var checked = {};
14741            var attributes = $target.attributes;
14742            var attr;
14743            var name;
14744            var i = attributes.length;
14745            while (i--) {
14746                attr = attributes[i];
14747                name = attr.name;
14748                if (!filter || has(filter, name)) {
14749                    if (getAttributeValue($target, attr) !== $oldstate[name]) {
14750                        // The pushing is redundant but gzips very nicely
14751                        mutations.push(MutationRecord({
14752                            type: "attributes",
14753                            target: $target,
14754                            attributeName: name,
14755                            oldValue: $oldstate[name],
14756                            attributeNamespace: attr.namespaceURI // in ie<8 it incorrectly will return undefined
14757                        }));
14758                    }
14759                    checked[name] = true;
14760                }
14761            }
14762            for (name in $oldstate) {
14763                if (!(checked[name])) {
14764                    mutations.push(MutationRecord({
14765                        target: $target,
14766                        type: "attributes",
14767                        attributeName: name,
14768                        oldValue: $oldstate[name]
14769                    }));
14770                }
14771            }
14772        }
14773
14774        /**
14775         * searchSubtree: array of mutations so far, element, element clone, bool
14776         * synchronous dfs comparision of two nodes
14777         * This function is applied to any observed element with childList or subtree specified
14778         * Sorry this is kind of confusing as shit, tried to comment it a bit...
14779         * codereview.stackexchange.com/questions/38351 discussion of an earlier version of this func
14780         *
14781         * @param {Array} mutations
14782         * @param {Node} $target
14783         * @param {!Object} $oldstate : A custom cloned node from clone()
14784         * @param {!Object} config : A custom mutation config
14785         */
14786        function searchSubtree(mutations, $target, $oldstate, config) {
14787            // Track if the tree is dirty and has to be recomputed (#14).
14788            var dirty;
14789            /*
14790             * Helper to identify node rearrangment and stuff...
14791             * There is no gaurentee that the same node will be identified for both added and removed nodes
14792             * if the positions have been shuffled.
14793             * conflicts array will be emptied by end of operation
14794             */
14795            function resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes) {
14796                // the distance between the first conflicting node and the last
14797                var distance = conflicts.length - 1;
14798                // prevents same conflict being resolved twice consider when two nodes switch places.
14799                // only one should be given a mutation event (note -~ is used as a math.ceil shorthand)
14800                var counter = -~((distance - numAddedNodes) / 2);
14801                var $cur;
14802                var oldstruct;
14803                var conflict;
14804                while ((conflict = conflicts.pop())) {
14805                    $cur = $kids[conflict.i];
14806                    oldstruct = $oldkids[conflict.j];
14807
14808                    // attempt to determine if there was node rearrangement... won't gaurentee all matches
14809                    // also handles case where added/removed nodes cause nodes to be identified as conflicts
14810                    if (config.kids && counter && Math.abs(conflict.i - conflict.j) >= distance) {
14811                        mutations.push(MutationRecord({
14812                            type: "childList",
14813                            target: node,
14814                            addedNodes: [$cur],
14815                            removedNodes: [$cur],
14816                            // haha don't rely on this please
14817                            nextSibling: $cur.nextSibling,
14818                            previousSibling: $cur.previousSibling
14819                        }));
14820                        counter--; // found conflict
14821                    }
14822
14823                    // Alright we found the resorted nodes now check for other types of mutations
14824                    if (config.attr && oldstruct.attr) findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter);
14825                    if (config.charData && $cur.nodeType === 3 && $cur.nodeValue !== oldstruct.charData) {
14826                        mutations.push(MutationRecord({
14827                            type: "characterData",
14828                            target: $cur,
14829                            oldValue: oldstruct.charData
14830                        }));
14831                    }
14832                    // now look @ subtree
14833                    if (config.descendents) findMutations($cur, oldstruct);
14834                }
14835            }
14836
14837            /**
14838             * Main worker. Finds and adds mutations if there are any
14839             * @param {Node} node
14840             * @param {!Object} old : A cloned data structure using internal clone
14841             */
14842            function findMutations(node, old) {
14843                var $kids = node.childNodes;
14844                var $oldkids = old.kids;
14845                var klen = $kids.length;
14846                // $oldkids will be undefined for text and comment nodes
14847                var olen = $oldkids ? $oldkids.length : 0;
14848                // if (!olen && !klen) return; // both empty; clearly no changes
14849
14850                // we delay the intialization of these for marginal performance in the expected case (actually quite signficant on large subtrees when these would be otherwise unused)
14851                // map of checked element of ids to prevent registering the same conflict twice
14852                var map;
14853                // array of potential conflicts (ie nodes that may have been re arranged)
14854                var conflicts;
14855                var id; // element id from getElementId helper
14856                var idx; // index of a moved or inserted element
14857
14858                var oldstruct;
14859                // current and old nodes
14860                var $cur;
14861                var $old;
14862                // track the number of added nodes so we can resolve conflicts more accurately
14863                var numAddedNodes = 0;
14864
14865                // iterate over both old and current child nodes at the same time
14866                var i = 0, j = 0;
14867                // while there is still anything left in $kids or $oldkids (same as i < $kids.length || j < $oldkids.length;)
14868                while (i < klen || j < olen) {
14869                    // current and old nodes at the indexs
14870                    $cur = $kids[i];
14871                    oldstruct = $oldkids[j];
14872                    $old = oldstruct && oldstruct.node;
14873
14874                    if ($cur === $old) { // expected case - optimized for this case
14875                        // check attributes as specified by config
14876                        if (config.attr && oldstruct.attr) /* oldstruct.attr instead of textnode check */findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter);
14877                        // check character data if node is a comment or textNode and it's being observed
14878                        if (config.charData && oldstruct.charData !== undefined && $cur.nodeValue !== oldstruct.charData) {
14879                            mutations.push(MutationRecord({
14880                                type: "characterData",
14881                                target: $cur,
14882                                oldValue: oldstruct.charData
14883                            }));
14884                        }
14885
14886                        // resolve conflicts; it will be undefined if there are no conflicts - otherwise an array
14887                        if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes);
14888
14889                        // recurse on next level of children. Avoids the recursive call when there are no children left to iterate
14890                        if (config.descendents && ($cur.childNodes.length || oldstruct.kids && oldstruct.kids.length)) findMutations($cur, oldstruct);
14891
14892                        i++;
14893                        j++;
14894                    } else { // (uncommon case) lookahead until they are the same again or the end of children
14895                        dirty = true;
14896                        if (!map) { // delayed initalization (big perf benefit)
14897                            map = {};
14898                            conflicts = [];
14899                        }
14900                        if ($cur) {
14901                            // check id is in the location map otherwise do a indexOf search
14902                            if (!(map[id = getElementId($cur)])) { // to prevent double checking
14903                                // mark id as found
14904                                map[id] = true;
14905                                // custom indexOf using comparitor checking oldkids[i].node === $cur
14906                                if ((idx = indexOfCustomNode($oldkids, $cur, j)) === -1) {
14907                                    if (config.kids) {
14908                                        mutations.push(MutationRecord({
14909                                            type: "childList",
14910                                            target: node,
14911                                            addedNodes: [$cur], // $cur is a new node
14912                                            nextSibling: $cur.nextSibling,
14913                                            previousSibling: $cur.previousSibling
14914                                        }));
14915                                        numAddedNodes++;
14916                                    }
14917                                } else {
14918                                    conflicts.push({ // add conflict
14919                                        i: i,
14920                                        j: idx
14921                                    });
14922                                }
14923                            }
14924                            i++;
14925                        }
14926
14927                        if ($old &&
14928                            // special case: the changes may have been resolved: i and j appear congurent so we can continue using the expected case
14929                            $old !== $kids[i]
14930                        ) {
14931                            if (!(map[id = getElementId($old)])) {
14932                                map[id] = true;
14933                                if ((idx = indexOf($kids, $old, i)) === -1) {
14934                                    if (config.kids) {
14935                                        mutations.push(MutationRecord({
14936                                            type: "childList",
14937                                            target: old.node,
14938                                            removedNodes: [$old],
14939                                            nextSibling: $oldkids[j + 1], // praise no indexoutofbounds exception
14940                                            previousSibling: $oldkids[j - 1]
14941                                        }));
14942                                        numAddedNodes--;
14943                                    }
14944                                } else {
14945                                    conflicts.push({
14946                                        i: idx,
14947                                        j: j
14948                                    });
14949                                }
14950                            }
14951                            j++;
14952                        }
14953                    }// end uncommon case
14954                }// end loop
14955
14956                // resolve any remaining conflicts
14957                if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes);
14958            }
14959            findMutations($target, $oldstate);
14960            return dirty;
14961        }
14962
14963        /**
14964         * Utility
14965         * Cones a element into a custom data structure designed for comparision. https://gist.github.com/megawac/8201012
14966         *
14967         * @param {Node} $target
14968         * @param {!Object} config : A custom mutation config
14969         * @return {!Object} : Cloned data structure
14970         */
14971        function clone($target, config) {
14972            var recurse = true; // set true so childList we'll always check the first level
14973            return (function copy($target) {
14974                var elestruct = {
14975                    /** @type {Node} */
14976                    node: $target
14977                };
14978
14979                // Store current character data of target text or comment node if the config requests
14980                // those properties to be observed.
14981                if (config.charData && ($target.nodeType === 3 || $target.nodeType === 8)) {
14982                    elestruct.charData = $target.nodeValue;
14983                }
14984                // its either a element, comment, doc frag or document node
14985                else {
14986                    // Add attr only if subtree is specified or top level and avoid if
14987                    // attributes is a document object (#13).
14988                    if (config.attr && recurse && $target.nodeType === 1) {
14989                        /**
14990                         * clone live attribute list to an object structure {name: val}
14991                         * @type {Object.<string, string>}
14992                         */
14993                        elestruct.attr = reduce($target.attributes, function
14993(memo, attr) {
14994                            if (!config.afilter || config.afilter[attr.name]) {
14995                                memo[attr.name] = getAttributeValue($target, attr);
14996                            }
14997                            return memo;
14998                        }, {});
14999                    }
15000
15001                    // whether we should iterate the children of $target node
15002                    if (recurse && ((config.kids || config.charData) || (config.attr && config.descendents))) {
15003                        /** @type {Array.<!Object>} : Array of custom clone */
15004                        elestruct.kids = map($target.childNodes, copy);
15005                    }
15006
15007                    recurse = config.descendents;
15008                }
15009                return elestruct;
15010            })($target);
15011        }
15012
15013        /**
15014         * indexOf an element in a collection of custom nodes
15015         *
15016         * @param {NodeList} set
15017         * @param {!Object} $node : A custom cloned node
15018         * @param {number} idx : index to start the loop
15019         * @return {number}
15020         */
15021        function indexOfCustomNode(set, $node, idx) {
15022            return indexOf(set, $node, idx, JSCompiler_renameProperty("node"));
15023        }
15024
15025        // using a non id (eg outerHTML or nodeValue) is extremely naive and will run into issues with nodes that may appear the same like <li></li>
15026        var counter = 1; // don't use 0 as id (falsy)
15027        /** @const */
15028        var expando = "mo_id";
15029
15030        /**
15031         * Attempt to uniquely id an element for hashing. We could optimize this for legacy browsers but it hopefully wont be called enough to be a concern
15032         *
15033         * @param {Node} $ele
15034         * @return {(string|number)}
15035         */
15036        function getElementId($ele) {
15037            try {
15038                return $ele.id || ($ele[expando] = $ele[expando] || counter++);
15039            } catch (o_O) { // ie <8 will throw if you set an unknown property on a text node
15040                try {
15041                    return $ele.nodeValue; // naive
15042                } catch (shitie) { // when text node is removed: https://gist.github.com/megawac/8355978 :(
15043                    return counter++;
15044                }
15045            }
15046        }
15047
15048        /**
15049         * **map** Apply a mapping function to each item of a set
15050         * @param {Array|NodeList} set
15051         * @param {Function} iterator
15052         */
15053        function map(set, iterator) {
15054            var results = [];
15055            for (var index = 0; index < set.length; index++) {
15056                results[index] = iterator(set[index], index, set);
15057            }
15058            return results;
15059        }
15060
15061        /**
15062         * **Reduce** builds up a single result from a list of values
15063         * @param {Array|NodeList|NamedNodeMap} set
15064         * @param {Function} iterator
15065         * @param {*} [memo] Initial value of the memo.
15066         */
15067        function reduce(set, iterator, memo) {
15068            for (var index = 0; index < set.length; index++) {
15069                memo = iterator(memo, set[index], index, set);
15070            }
15071            return memo;
15072        }
15073
15074        /**
15075         * **indexOf** find index of item in collection.
15076         * @param {Array|NodeList} set
15077         * @param {Object} item
15078         * @param {number} idx
15079         * @param {string} [prop] Property on set item to compare to item
15080         */
15081        function indexOf(set, item, idx, prop) {
15082            for (/*idx = ~~idx*/; idx < set.length; idx++) {// start idx is always given as this is internal
15083                if ((prop ? set[idx][prop] : set[idx]) === item) return idx;
15084            }
15085            return -1;
15086        }
15087
15088        /**
15089         * @param {Object} obj
15090         * @param {(string|number)} prop
15091         * @return {boolean}
15092         */
15093        function has(obj, prop) {
15094            return obj[prop] !== undefined; // will be nicely inlined by gcc
15095        }
15096
15097        // GCC hack see https://stackoverflow.com/a/23202438/1517919
15098        function JSCompiler_renameProperty(a) {
15099            return a;
15100        }
15101
15102        return MutationObserver;
15103    })(void 0);
15104
15105/**
15106 * Copyright 2016 Google Inc. All Rights Reserved.
15107 *
15108 * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.
15109 *
15110 *  https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
15111 *
15112 */
15113(function(window, document) {
15114    'use strict';
15115
15116// Exits early if all IntersectionObserver and IntersectionObserverEntry
15117// features are natively supported.
15118    if ('IntersectionObserver' in window &&
15119        'IntersectionObserverEntry' in window &&
15120        'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
15121
15122        // Minimal polyfill for Edge 15's lack of `isIntersecting`
15123        // See: https://github.com/w3c/IntersectionObserver/issues/211
15124        if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
15125            Object.defineProperty(window.IntersectionObserverEntry.prototype,
15126                'isIntersecting', {
15127                    get: function () {
15128                        return this.intersectionRatio > 0;
15129                    }
15130                });
15131        }
15132        return;
15133    }
15134
15135    /**
15136     * An IntersectionObserver registry. This registry exists to hold a strong
15137     * reference to IntersectionObserver instances currently observering a target
15138     * element. Without this registry, instances without another reference may be
15139     * garbage collected.
15140     */
15141    var registry = [];
15142
15143
15144    /**
15145     * Creates the global IntersectionObserverEntry constructor.
15146     * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
15147     * @param {Object} entry A dictionary of instance properties.
15148     * @constructor
15149     */
15150    function IntersectionObserverEntry(entry) {
15151        this.time = entry.time;
15152        this.target = entry.target;
15153        this.rootBounds = entry.rootBounds;
15154        this.boundingClientRect = entry.boundingClientRect;
15155        this.intersectionRect = entry.intersectionRect || getEmptyRect();
15156        this.isIntersecting = !!entry.intersectionRect;
15157
15158        // Calculates the intersection ratio.
15159        var targetRect = this.boundingClientRect;
15160        var targetArea = targetRect.width * targetRect.height;
15161        var intersectionRect = this.intersectionRect;
15162        var intersectionArea = intersectionRect.width * intersectionRect.height;
15163
15164        // Sets intersection ratio.
15165        if (targetArea) {
15166            this.intersectionRatio = intersectionArea / targetArea;
15167        } else {
15168            // If area is zero and is intersecting, sets to 1, otherwise to 0
15169            this.intersectionRatio = this.isIntersecting ? 1 : 0;
15170        }
15171    }
15172
15173
15174    /**
15175     * Creates the global IntersectionObserver constructor.
15176     * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface
15177     * @param {Function} callback The function to be invoked after intersection
15178     *     changes have queued. The function is not invoked if the queue has
15179     *     been emptied by calling the `takeRecords` method.
15180     * @param {Object=} opt_options Optional configuration options.
15181     * @constructor
15182     */
15183    function IntersectionObserver(callback, opt_options) {
15184
15185        var options = opt_options || {};
15186
15187        if (typeof callback !== 'function') {
15188            throw new Error('callback must be a function');
15189        }
15190
15191        if (options.root && options.root.nodeType != 1) {
15192            throw new Error('root must be an Element');
15193        }
15194
15195        // Binds and throttles `this._checkForIntersections`.
15196        this._checkForIntersections = throttle(
15197            this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT);
15198
15199        // Private properties.
15200        this._callback = callback;
15201        this._observationTargets = [];
15202        this._queuedEntries = [];
15203        this._rootMarginValues = this._parseRootMargin(options.rootMargin);
15204
15205        // Public properties.
15206        this.thresholds = this._initThresholds(options.threshold);
15207        this.root = options.root || null;
15208        this.rootMargin = this._rootMarginValues.map(function(margin) {
15209            return margin.value + margin.unit;
15210        }).join(' ');
15211    }
15212
15213
15214    /**
15215     * The minimum interval within which the document will be checked for
15216     * intersection changes.
15217     */
15218    IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;
15219
15220
15221    /**
15222     * The frequency in which the polyfill polls for intersection changes.
15223     * this can be updated on a per instance basis and must be set prior to
15224     * calling `observe` on the first target.
15225     */
15226    IntersectionObserver.prototype.POLL_INTERVAL = null;
15227
15228    /**
15229     * Use a mutation observer on the root element
15230     * to detect intersection changes.
15231     */
15232    IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;
15233
15234
15235    /**
15236     * Starts observing a target element for intersection changes based on
15237     * the thresholds values.
15238     * @param {Element} target The DOM element to observe.
15239     */
15240    IntersectionObserver.prototype.observe = function(target) {
15241        var isTargetAlreadyObserved = this._observationTargets.some(function(item) {
15242            return item.element == target;
15243        });
15244
15245        if (isTargetAlreadyObserved) {
15246            return;
15247        }
15248
15249        if (!(target && target.nodeType == 1)) {
15250            throw new Error('target must be an Element');
15251        }
15252
15253        this._registerInstance();
15254        this._observationTargets.push({element: target, entry: null});
15255        this._monitorIntersections();
15256        this._checkForIntersections();
15257    };
15258
15259
15260    /**
15261     * Stops observing a target element for intersection changes.
15262     * @param {Element} target The DOM element to observe.
15263     */
15264    IntersectionObserver.prototype.unobserve = function(target) {
15265        this._observationTargets =
15266            this._observationTargets.filter(function(item) {
15267
15268                return item.element != target;
15269            });
15270        if (!this._observationTargets.length) {
15271            this._unmonitorIntersections();
15272            this._unregisterInstance();
15273        }
15274    };
15275
15276
15277    /**
15278     * Stops observing all target elements for intersection changes.
15279     */
15280    IntersectionObserver.prototype.disconnect = function() {
15281        this._observationTargets = [];
15282        this._unmonitorIntersections();
15283        this._unregisterInstance();
15284    };
15285
15286
15287    /**
15288     * Returns any queue entries that have not yet been reported to the
15289     * callback and clears the queue. This can be used in conjunction with the
15290     * callback to obtain the absolute most up-to-date intersection information.
15291     * @return {Array} The currently queued entries.
15292     */
15293    IntersectionObserver.prototype.takeRecords = function() {
15294        var records = this._queuedEntries.slice();
15295        this._queuedEntries = [];
15296        return records;
15297    };
15298
15299
15300    /**
15301     * Accepts the threshold value from the user configuration object and
15302     * returns a sorted array of unique threshold values. If a value is not
15303     * between 0 and 1 and error is thrown.
15304     * @private
15305     * @param {Array|number=} opt_threshold An optional threshold value or
15306     *     a list of threshold values, defaulting to [0].
15307     * @return {Array} A sorted list of unique and valid threshold values.
15308     */
15309    IntersectionObserver.prototype._initThresholds = function(opt_threshold) {
15310        var threshold = opt_threshold || [0];
15311        if (!Array.isArray(threshold)) threshold = [threshold];
15312
15313        return threshold.sort().filter(function(t, i, a) {
15314            if (typeof t !== 'number' || isNaN(t) || t < 0 || t > 1) {
15315                throw new Error('threshold must be a number between 0 and 1 inclusively');
15316            }
15317            return t !== a[i - 1];
15318        });
15319    };
15320
15321
15322    /**
15323     * Accepts the rootMargin value from the user configuration object
15324     * and returns an array of the four margin values as an object c
15324ontaining
15325     * the value and unit properties. If any of the values are not properly
15326     * formatted or use a unit other than px or %, and error is thrown.
15327     * @private
15328     * @param {string=} opt_rootMargin An optional rootMargin value,
15329     *     defaulting to '0px'.
15330     * @return {Array<Object>} An array of margin objects with the keys
15331     *     value and unit.
15332     */
15333    IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) {
15334        var marginString = opt_rootMargin || '0px';
15335        var margins = marginString.split(/\s+/).map(function(margin) {
15336            var parts = /^(-?\d*\.?\d+)(px|%)$/.exec(margin);
15337            if (!parts) {
15338                throw new Error('rootMargin must be specified in pixels or percent');
15339            }
15340            return {value: parseFloat(parts[1]), unit: parts[2]};
15341        });
15342
15343        // Handles shorthand.
15344        margins[1] = margins[1] || margins[0];
15345        margins[2] = margins[2] || margins[0];
15346        margins[3] = margins[3] || margins[1];
15347
15348        return margins;
15349    };
15350
15351
15352    /**
15353     * Starts polling for intersection changes if the polling is not already
15354     * happening, and if the page's visibilty state is visible.
15355     * @private
15356     */
15357    IntersectionObserver.prototype._monitorIntersections = function() {
15358        if (!this._monitoringIntersections) {
15359            this._monitoringIntersections = true;
15360
15361            // If a poll interval is set, use polling instead of listening to
15362            // resize and scroll events or DOM mutations.
15363            if (this.POLL_INTERVAL) {
15364                this._monitoringInterval = setInterval(
15365                    this._checkForIntersections, this.POLL_INTERVAL);
15366            }
15367            else {
15368                addEvent(window, 'resize', this._checkForIntersections, true);
15369                addEvent(document, 'scroll', this._checkForIntersections, true);
15370
15371                if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in window) {
15372                    this._domObserver = new MutationObserver(this._checkForIntersections);
15373                    this._domObserver.observe(document, {
15374                        attributes: true,
15375                        childList: true,
15376                        characterData: true,
15377                        subtree: true
15378                    });
15379                }
15380            }
15381        }
15382    };
15383
15384
15385    /**
15386     * Stops polling for intersection changes.
15387     * @private
15388     */
15389    IntersectionObserver.prototype._unmonitorIntersections = function() {
15390        if (this._monitoringIntersections) {
15391            this._monitoringIntersections = false;
15392
15393            clearInterval(this._monitoringInterval);
15394            this._monitoringInterval = null;
15395
15396            removeEvent(window, 'resize', this._checkForIntersections, true);
15397            removeEvent(document, 'scroll', this._checkForIntersections, true);
15398
15399            if (this._domObserver) {
15400                this._domObserver.disconnect();
15401                this._domObserver = null;
15402            }
15403        }
15404    };
15405
15406
15407    /**
15408     * Scans each observation target for intersection changes and adds them
15409     * to the internal entries queue. If new entries are found, it
15410     * schedules the callback to be invoked.
15411     * @private
15412     */
15413    IntersectionObserver.prototype._checkForIntersections = function() {
15414        var rootIsInDom = this._rootIsInDom();
15415        var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect();
15416
15417        this._observationTargets.forEach(function(item) {
15418            var target = item.element;
15419            var targetRect = getBoundingClientRect(target);
15420            var rootContainsTarget = this._rootContainsTarget(target);
15421            var oldEntry = item.entry;
15422            var intersectionRect = rootIsInDom && rootContainsTarget &&
15423                this._computeTargetAndRootIntersection(target, rootRect);
15424
15425            var newEntry = item.entry = new IntersectionObserverEntry({
15426                time: now(),
15427                target: target,
15428                boundingClientRect: targetRect,
15429                rootBounds: rootRect,
15430                intersectionRect: intersectionRect
15431            });
15432
15433            if (!oldEntry) {
15434                this._queuedEntries.push(newEntry);
15435            } else if (rootIsInDom && rootContainsTarget) {
15436                // If the new entry intersection ratio has crossed any of the
15437                // thresholds, add a new entry.
15438                if (this._hasCrossedThreshold(oldEntry, newEntry)) {
15439                    this._queuedEntries.push(newEntry);
15440                }
15441            } else {
15442                // If the root is not in the DOM or target is not contained within
15443                // root but the previous entry for this target had an intersection,
15444                // add a new record indicating removal.
15445                if (oldEntry && oldEntry.isIntersecting) {
15446                    this._queuedEntries.push(newEntry);
15447                }
15448            }
15449        }, this);
15450
15451        if (this._queuedEntries.length) {
15452            this._callback(this.takeRecords(), this);
15453        }
15454    };
15455
15456
15457    /**
15458     * Accepts a target and root rect computes the intersection between then
15459     * following the algorithm in the spec.
15460     * TODO(philipwalton): at this time clip-path is not considered.
15461     * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo
15462     * @param {Element} target The target DOM element
15463     * @param {Object} rootRect The bounding rect of the root after being
15464     *     expanded by the rootMargin value.
15465     * @return {?Object} The final intersection rect object or undefined if no
15466     *     intersection is found.
15467     * @private
15468     */
15469    IntersectionObserver.prototype._computeTargetAndRootIntersection =
15470        function(target, rootRect) {
15471
15472            // If the element isn't displayed, an intersection can't happen.
15473            if (window.getComputedStyle(target).display == 'none') return;
15474
15475            var targetRect = getBoundingClientRect(target);
15476            var intersectionRect = targetRect;
15477            var parent = getParentNode(target);
15478            var atRoot = false;
vendor: 9,839 bytes, lines 15479-15749
15479
15480            while (!atRoot) {
15481                var parentRect = null;
15482                var parentComputedStyle = parent.nodeType == 1 ?
15483                    window.getComputedStyle(parent) : {};
15484
15485                // If the parent isn't displayed, an intersection can't happen.
15486                if (parentComputedStyle.display == 'none') return;
15487
15488                if (parent == this.root || parent == document) {
15489                    atRoot = true;
15490                    parentRect = rootRect;
15491                } else {
15492                    // If the element has a non-visible overflow, and it's not the <body>
15493                    // or <html> element, update the intersection rect.
15494                    // Note: <body> and <html> cannot be clipped to a rect that's not also
15495                    // the document rect, so no need to compute a new intersection.
15496                    if (parent != document.body &&
15497                        parent != document.documentElement &&
15498                        parentComputedStyle.overflow != 'visible') {
15499                        parentRect = getBoundingClientRect(parent);
15500                    }
15501                }
15502
15503                // If either of the above conditionals set a new parentRect,
15504                // calculate new intersection data.
15505                if (parentRect) {
15506                    intersectionRect = computeRectIntersection(parentRect, intersectionRect);
15507
15508                    if (!intersectionRect) break;
15509                }
15510                parent = getParentNode(parent);
15511            }
15512            return intersectionRect;
15513        };
15514
15515
15516    /**
15517     * Returns the root rect after being expanded by the rootMargin value.
15518     * @return {Object} The expanded root rect.
15519     * @private
15520     */
15521    IntersectionObserver.prototype._getRootRect = function() {
15522        var rootRect;
15523        if (this.root) {
15524            rootRect = getBoundingClientRect(this.root);
15525        } else {
15526            // Use <html>/<body> instead of window since scroll bars affect size.
15527            var html = document.documentElement;
15528            var body = document.body;
15529            rootRect = {
15530                top: 0,
15531                left: 0,
15532                right: html.clientWidth || body.clientWidth,
15533                width: html.clientWidth || body.clientWidth,
15534                bottom: html.clientHeight || body.clientHeight,
15535                height: html.clientHeight || body.clientHeight
15536            };
15537        }
15538        return this._expandRectByRootMargin(rootRect);
15539    };
15540
15541
15542    /**
15543     * Accepts a rect and expands it by the rootMargin value.
15544     * @param {Object} rect The rect object to expand.
15545     * @return {Object} The expanded rect.
15546     * @private
15547     */
15548    IntersectionObserver.prototype._expandRectByRootMargin = function(rect) {
15549        var margins = this._rootMarginValues.map(function(margin, i) {
15550            return margin.unit == 'px' ? margin.value :
15551                margin.value * (i % 2 ? rect.width : rect.height) / 100;
15552        });
15553        var newRect = {
15554            top: rect.top - margins[0],
15555            right: rect.right + margins[1],
15556            bottom: rect.bottom + margins[2],
15557            left: rect.left - margins[3]
15558        };
15559        newRect.width = newRect.right - newRect.left;
15560        newRect.height = newRect.bottom - newRect.top;
15561
15562        return newRect;
15563    };
15564
15565
15566    /**
15567     * Accepts an old and new entry and returns true if at least one of the
15568     * threshold values has been crossed.
15569     * @param {?IntersectionObserverEntry} oldEntry The previous entry for a
15570     *    particular target element or null if no previous entry exists.
15571     * @param {IntersectionObserverEntry} newEntry The current entry for a
15572     *    particular target element.
15573     * @return {boolean} Returns true if a any threshold has been crossed.
15574     * @private
15575     */
15576    IntersectionObserver.prototype._hasCrossedThreshold =
15577        function(oldEntry, newEntry) {
15578
15579            // To make comparing easier, an entry that has a ratio of 0
15580            // but does not actually intersect is given a value of -1
15581            var oldRatio = oldEntry && oldEntry.isIntersecting ?
15582                oldEntry.intersectionRatio || 0 : -1;
15583            var newRatio = newEntry.isIntersecting ?
15584                newEntry.intersectionRatio || 0 : -1;
15585
15586            // Ignore unchanged ratios
15587            if (oldRatio === newRatio) return;
15588
15589            for (var i = 0; i < this.thresholds.length; i++) {
15590                var threshold = this.thresholds[i];
15591
15592                // Return true if an entry matches a threshold or if the new ratio
15593                // and the old ratio are on the opposite sides of a threshold.
15594                if (threshold == oldRatio || threshold == newRatio ||
15595                    threshold < oldRatio !== threshold < newRatio) {
15596                    return true;
15597                }
15598            }
15599        };
15600
15601
15602    /**
15603     * Returns whether or not the root element is an element and is in the DOM.
15604     * @return {boolean} True if the root element is an element and is in the DOM.
15605     * @private
15606     */
15607    IntersectionObserver.prototype._rootIsInDom = function() {
15608        return !this.root || containsDeep(document, this.root);
15609    };
15610
15611
15612    /**
15613     * Returns whether or not the target element is a child of root.
15614     * @param {Element} target The target element to check.
15615     * @return {boolean} True if the target element is a child of root.
15616     * @private
15617     */
15618    IntersectionObserver.prototype._rootContainsTarget = function(target) {
15619        return containsDeep(this.root || document, target);
15620    };
15621
15622
15623    /**
15624     * Adds the instance to the global IntersectionObserver registry if it isn't
15625     * already present.
15626     * @private
15627     */
15628    IntersectionObserver.prototype._registerInstance = function() {
15629        if (registry.indexOf(this) < 0) {
15630            registry.push(this);
15631        }
15632    };
15633
15634
15635    /**
15636     * Removes the instance from the global IntersectionObserver registry.
15637     * @private
15638     */
15639    IntersectionObserver.prototype._unregisterInstance = function() {
15640        var index = registry.indexOf(this);
15641        if (index != -1) registry.splice(index, 1);
15642    };
15643
15644
15645    /**
15646     * Returns the result of the performance.now() method or null in browsers
15647     * that don't support the API.
15648     * @return {number} The elapsed time since the page was requested.
15649     */
15650    function now() {
15651        return window.performance && performance.now && performance.now();
15652    }
15653
15654
15655    /**
15656     * Throttles a function and delays its executiong, so it's only called at most
15657     * once within a given time period.
15658     * @param {Function} fn The function to throttle.
15659     * @param {number} timeout The amount of time that must pass before the
15660     *     function can be called again.
15661     * @return {Function} The throttled function.
15662     */
15663    function throttle(fn, timeout) {
15664        var timer = null;
15665        return function () {
15666            if (!timer) {
15667                timer = setTimeout(function() {
15668                    fn();
15669                    timer = null;
15670                }, timeout);
15671            }
15672        };
15673    }
15674
15675
15676    /**
15677     * Adds an event handler to a DOM node ensuring cross-browser compatibility.
15678     * @param {Node} node The DOM node to add the event handler to.
15679     * @param {string} event The event name.
15680     * @param {Function} fn The event handler to add.
15681     * @param {boolean} opt_useCapture Optionally adds the even to the capture
15682     *     phase. Note: this only works in modern browsers.
15683     */
15684    function addEvent(node, event, fn, opt_useCapture) {
15685        if (typeof node.addEventListener === 'function') {
15686            node.addEventListener(event, fn, opt_useCapture || false);
15687        }
15688        else if (typeof node.attachEvent === 'function') {
15689            node.attachEvent('on' + event, fn);
15690        }
15691    }
15692
15693
15694    /**
15695     * Removes a previously added event handler from a DOM node.
15696     * @param {Node} node The DOM node to remove the event handler from.
15697     * @param {string} event The event name.
15698     * @param {Function} fn The event handler to remove.
15699     * @param {boolean} opt_useCapture If the event handler was added with this
15700     *     flag set to true, it should be set to true here in order to remove it.
15701     */
15702    function removeEvent(node, event, fn, opt_useCapture) {
15703        if (typeof node.removeEventListener === 'function') {
15704            node.removeEventListener(event, fn, opt_useCapture || false);
15705        }
15706        else if (typeof node.detatchEvent === 'function') {
15707            node.detatchEvent('on' + event, fn);
15708        }
15709    }
15710
15711
15712    /**
15713     * Returns the intersection between two rect objects.
15714     * @param {Object} rect1 The first rect.
15715     * @param {Object} rect2 The second rect.
15716     * @return {?Object} The intersection rect or undefined if no intersection
15717     *     is found.
15718     */
15719    function computeRectIntersection(rect1, rect2) {
15720        var top = Math.max(rect1.top, rect2.top);
15721        var bottom = Math.min(rect1.bottom, rect2.bottom);
15722        var left = Math.max(rect1.left, rect2.left);
15723        var right = Math.min(rect1.right, rect2.right);
15724        var width = right - left;
15725        var height = bottom - top;
15726
15727        return (width >= 0 && height >= 0) && {
15728            top: top,
15729            bottom: bottom,
15730            left: left,
15731            right: right,
15732            width: width,
15733            height: height
15734        };
15735    }
15736
15737
15738    /**
15739     * Shims the native getBoundingClientRect for compatibility with older IE.
15740     * @param {Element} el The element whose bounding rect to get.
15741     * @return {Object} The (possibly shimmed) rect of the element.
15742     */
15743    function getBoundingClientRect(el) {
15744        var rect;
15745
15746        try {
15747            rect = el.getBoundingClientRect();
15748        } catch (err) {
15749            // Ignore Windows 7 IE11 "Unspecified error"
15750            // https://github.com/w3c/IntersectionObserver/pull/205
15751        }
15752
15753        if (!rect) return getEmptyRect();
15754
15755        // Older IE
15756        if (!(rect.width && rect.height)) {
15757            rect = {
15758                top: rect.top,
15759                right: rect.right,
15760                bottom: rect.bottom,
15761                left: rect.left,
15762                width: rect.right - rect.left,
15763                height: rect.bottom - rect.top
15764            };
15765        }
15766        return rect;
15767    }
15768
15769
15770    /**
15771     * Returns an empty rect object. An empty rect is returned when an element
15772     * is not in the DOM.
15773     * @return {Object} The empty rect.
15774     */
15775    function getEmptyRect() {
15776        return {
15777            top: 0,
15778            bottom: 0,
15779            left: 0,
15780            right: 0,
15781            width: 0,
15782            height: 0
15783        };
15784    }
15785
15786    /**
15787     * Checks to see if a parent element contains a child elemnt (including inside
15788     * shadow DOM).
15789     * @param {Node} parent The parent element.
15790     * @param {Node} child The child element.
15791     * @return {boolean} True if the parent node contains the child node.
15792     */
15793    function containsDeep(parent, child) {
15794        var node = child;
15795        while (node) {
15796            if (node == parent) return true;
15797
15798            node = getParentNode(node);
15799        }
15800        return false;
15801    }
15802
15803
15804    /**
15805     * Gets the parent node of an element or its host element if the parent node
15806     * is a shadow root.
15807     * @param {Node} node The node whose parent to get.
15808     * @return {Node|null} The parent node or null if no parent exists.
15809     */
15810    function getParentNode(node) {
15811        var parent = node.parentNode;
15812
15813        if (parent && parent.nodeType == 11 && parent.host) {
15814            // If the parent is a shadow root, return the host element.
15815            return parent.host;
15816        }
15817        return parent;
15818    }
15819
15820
15821// Exposes the constructors globally.
15822    window.IntersectionObserver = IntersectionObserver;
15823    window.IntersectionObserverEntry = IntersectionObserverEntry;
15824
15825}(window, document));
15826
15827if (window.NodeList && !NodeList.prototype.forEach) {
15828    NodeList.prototype.forEach = function (callback, thisArg) {
15829        thisArg = thisArg || window;
15830        for (var i = 0; i < this.length; i++) {
15831            callback.call(thisArg, this[i], i, this);
15832        }
15833    };
15834}
15835
15836CAFE24.SMART_BANNER_DEFAULT = {
15837
15838    /**
15839     * ECHOSTING-331346 : 프론트화면에서 스마트배너 갱신시 화면 리로드
15840     * @returns {boolean}
15841     */
15842    reloadFrontPage: function () {
15843        addEventListener('message', function(e) {
15844
15845            if (e.origin.indexOf(EC_FRONT_JS_CONFIG_MANAGE.sDefaultAppDomain) < 0) {
15846                return false;
15847            }
15848
15849            var jsonData = JSON.parse(e.data);
15850
15851            if (jsonData == '' || jsonData.type == '') {
15852                return false;
15853            }
15854
15855            if (jsonData.type == 'frontReload') {
15856                window.location.reload();
15857                return true;
15858            }
15859
15860        }, false);
15861    }
15862};
15863
15864var SMART_BANNER_DEFAULT = CAFE24.getDeprecatedNamespace('SMART_BANNER_DEFAULT');
15865
15866EC$(function () {
15867
15868    try {
15869        // 스마트배너 모듈명이 존재하는지 확인
15870        var smartBannerModuleCount = EC$("div[module*='smart-banner-admin'],div[app4you-smart-banner*='smart-banner-admin']").length;
15871
15872        if (!smartBannerModuleCount) {
15873            return false;
15874        }
15875
15876        // SDK 가 로드되지 않은경우 필요값 설정
15877        if (typeof CAFE24API === "undefined") {
15878            CAFE24API = {
15879                MALL_ID: EC_FRONT_JS_CONFIG_MANAGE.sMallId,
15880                SHOP_NO: CAFE24.SDE_SHOP_NUM
15881            };
15882        }
15883
15884        // 스마트배너 모듈명이 존재할경우 ScriptTag Load
15885        var defaultAppScripts = document.createElement("script");
15886        defaultAppScripts.type = "text/javascript";
15887        defaultAppScripts.src = EC_FRONT_JS_CONFIG_MANAGE.sSmartBannerScriptUrl;
15888        EC$("head").append(defaultAppScripts);
15889
15890        // ECHOSTING-331346 : 프론트화면에서 스마트배너 갱신시 화면 리로드
15891        CAFE24.SMART_BANNER_DEFAULT.reloadFrontPage();
15892
15893    } catch (e) {
15894        if (typeof(e) === 'object' && e.stack && window.console) {
15895            console.log(e);
15896        }
15897    }
15898});
15899
15900CAFE24.KAKAO_PIXEL_BRIDGE = (function() {
15901    var bUseKakaoPixel = false;
15902    var oKakaoPixelInstance;
15903
15904    return {
15905
15906        /**
15907         * 카카오 픽셀 정보 세팅
15908         * @param sKakaoPixelId
15909         */
15910        init: function (sKakaoPixelId) {
15911            if (typeof kakaoPixel !== 'function' || typeof sKakaoPixelId === 'undefined') {
15912                return;
15913            }
15914
15915            if (CAFE24.KAKAO_PIXEL_BRIDGE.bUseKakaoPixel === true) {
15916                return;
15917            }
15918
15919            try {
15920                CAFE24.KAKAO_PIXEL_BRIDGE.oKakaoPixelInstance = kakaoPixel(sKakaoPixelId);
15921                CAFE24.KAKAO_PIXEL_BRIDGE.bUseKakaoPixel = true;
15922            } catch (error) {
15923                CAFE24.KAKAO_PIXEL_BRIDGE.bUseKakaoPixel = false;
15924            }
15925        },
15926
15927        /**
15928         * 상품 번호 반환
15929         * @param sCheckedProduct 선택된 상품
15930         * @returns {string}
15931         */
15932        getProductNo: function (sCheckedProduct) {
15933            return sCheckedProduct.split(':')[0];
15934        },
15935
15936        /**
15937         * 카카오픽셀 validation
15938         * @param sAction 이벤트 액션명
15939         * @returns {boolean}
15940         */
15941        getValidation: function(sAction)
15942        {
15943            return CAFE24.KAKAO_PIXEL_BRIDGE.bUseKakaoPixel === true && sAction in CAFE24.KAKAO_PIXEL_BRIDGE.oKakaoPixelInstance === true;
15944        },
15945
15946        /**
15947         * 기본 데이터 세팅
15948         * @param iProductNo 상품 번호
15949         * @returns {object}
15950         */
15951        getDefaultData: function(iProductNo) {
15952            var oData = {};
15953
15954            if (typeof iProductNo !== 'undefined') {
15955                oData['products'] = CAFE24.KAKAO_PIXEL_BRIDGE.getProductData([iProductNo]);
15956            }
15957
15958            return oData;
15959        },
15960
15961        /**
15962         * 상품 데이터 반환
15963         * @param {Array} aProductNo 상품번호
15964         * @returns {Array}
15965         */
15966        getProductData: function(aProductNo) {
15967            var aProductList = [];
15968
15969            if (Array.isArray(aProductNo) === false) {
15970                return aProductList;
15971            }
15972
15973            aProductNo.forEach(function(iProductNo) {
15974                aProductList.push({id: iProductNo});
15975            });
15976
15977            return aProductList;
15978        },
15979
15980        /**
15981         * 장바구니 등록 이벤트시 데이터 전송 처리
15982         * @param iProductNo 상품번호
15983         */
15984        addBasket: function (iProductNo) {
15985            if (CAFE24.KAKAO_PIXEL_BRIDGE.getValidation('addToCart') === false) {
15986                return;
15987            }
15988
15989            var oData = CAFE24.KAKAO_PIXEL_BRIDGE.getDefaultData(iProductNo);
15990            try {
15991                CAFE24.KAKAO_PIXEL_BRIDGE.oKakaoPixelInstance.addToCart(oData);
15992            } catch (error) {}
15993        },
15994
15995        /**
15996         * 관심상품 등록 이벤트시 데이터 전송 처리
15997         * @param iProductNo 상품번호
15998         */
15999        addWishList: function (iProductNo) {
16000            if (CAFE24.KAKAO_PIXEL_BRIDGE.getValidation('addToWishList') === false) {
16001                return;
16002            }
16003
16004            var oData = CAFE24.KAKAO_PIXEL_BRIDGE.getDefaultData(iProductNo);
16005            try {
16006                CAFE24.KAKAO_PIXEL_BRIDGE.oKakaoPixelInstance.addToWishList(oData);
16007            } catch (error) {}
16008        }
16009
16010    };
16011})();
16012
16013var EC_KakaoPixelBridge = CAFE24.getDeprecatedNamespace('KAKAO_PIXEL_BRIDGE');
16014CAPP_SHOP_FRONT_COMMON_UTIL = {
16015    findTargetFrame: function()
16016    {
16017        //팝업창 일경우에는 바로 opener를 반환
16018        if (CAPP_SHOP_FRONT_COMMON_UTIL.isPopupFromThisShopFront() === true) {
16019            return window.opener;
16020        }
16021
16022        try {
16023            var bIsIframe = false;
16024            var sUrl = document.location.pathname + document.location.search;
16025
16026            //parent의 프레임내용에서 현재주소와 동일 url을 가진 아이프레임이 있다면 아이프레임에서 실행된것으로 판단하고 parent를 반환
16027            EC$(parent.document).find('iframe').each(function() {
16028                if (sUrl === EC$(this).attr('src')) {
16029                    bIsIframe = true;
16030                    return false;
16031                }
16032            });
16033            if (bIsIframe === true) {
16034                return parent;
16035            }
16036        } catch (e) {}
16037
16038        //그 이외(일반페이지, 프레임셋)에서는 현재페이지에서 이동되는것으로 함
16039        return document;
16040    },
16041
16042    /**
16043     * 기존 코드와의 호환성 때문에 남겨둠
16044     * @return bool
16045     * @deprecated
16046     */
16047    isAdminOpener: function()
16048    {
16049        return this.isPopupFromThisShopFront();
16050    },
16051
16052    /**
16053     * 현재 창이 동일 쇼핑몰 내의 프론트에서 열려진 팝업창인지 리턴
16054     * @return bool 동일 쇼핑몰 내의 프론트에서 열려진 팝업창이면 true, 아니면 false
16055     */
16056    isPopupFromThisShopFront: function()
16057    {
16058        try {
16059            // 팝업창이 아니면 false 리턴
16060            if (window.opener === null) {
16061                return false;
16062            }
16063
16064            // 현재 창의 도메인과 opener의 도메인이 다르면 false 리턴
16065            if (window.location.host !== window.opener.location.host) {
16066                return false;
16067            }
16068
16069            // 어드민으로부터 열려진 경우 false 리턴
16070            var regAdminUrl = /^(\/admin\/php\/|\/disp\/admin\/)/;
16071            if (regAdminUrl.test(window.opener.location.pathname) === true) {
16072                return false;
16073            }
16074
16075            // 프론트로부터 열려진 경우이므로 true 리턴
16076            return true;
16077
16078        } catch (e) {
16079            // window.opener에 접근 불가능한 케이스는 이미 본 창이 닫혔거나 도메인이 다른 것이므로 false 리턴
16080            return false;
16081
16082        }
16083    },
16084
16085    /**
16086     * url에서 파라미터 가져오기
16087     * @param string name 파라미터명
16088     * @return string 파라미터 값
16089     */
16090     getParameterByName: function (name) {
16091        name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
16092        var regexS = "[\\?&]" + name + "=([^&#]*)";
16093        var regex = new RegExp(regexS);
16094        var results = regex.exec(window.location.href);
16095
16096        if (results == null) {
16097            return '';
16098        } else {
16099            return decodeURIComponent(results[1].replace(/\+/g, " "));
16100        }
16101    },
16102
16103    historyBack: function(sMsg)
16104    {
16105        if (typeof(sMsg) !== 'undefined' && sMsg !== '') {
16106            alert(sMsg);
16107        }
16108
16109        if (this.isPopupFromThisShopFront() === true) {
16110            opener = window;
16111            window.close();
16112        } else {
16113            history.back();
16114        }
16115    },
16116
16117    /**
16118     * ECHOSTING-627626 네이버페이 navercheckout 진입을 POST 폼 제출로 처리한다.
16119     * - 기존 GET 네비게이션(location.href/window.open) 대신 POST 폼으로 CA_JS 광고 폼 데이터(analycis_form_data)를 동봉
16120     *   (~5KB, 최대 50KB → 쿼리스트링 URL 길이 한계 회피)
16121     * - 기존 쿼리 파라미터는 hidden 으로 이관. CA_JS 값은 버튼 클릭 시점에 취득(예외격리, REQ-008)
16122     * - 대상 창(oTargetWin)의 document 에 폼을 만들어 제출 → target/window.name 조작 없이 해당 창을 이동
16123     * @param {string} sUrl navercheckout URL (쿼리 포함 가능)
16124     * @param {object} oTargetWin 이동 대상 window (self/opener/parent). 미지정 시 현재 창
16125     * @param {boolean} bNewWindow 새 창(window.open) 여부
16126     */
16127    submitNaverCheckout: function(sUrl, oTargetWin, bNewWindow)
16128    {
16129        var sAction = sUrl;
16130        var sQuery = '';
16131        var iPos = sUrl.indexOf('?');
16132        if (iPos >= 0) {
16133            sAction = sUrl.substring(0, iPos);
16134            sQuery = sUrl.substring(iPos + 1);
16135        }
16136
16137        // 새 창이면 현재 document 에 만들고 target 으로 새 창 지정, 아니면 대상 창 document 에 생성
16138        var oDoc = document;
16139        if (bNewWindow !== true && oTargetWin && oTargetWin.document && oTargetWin !== window) {
16140            try {
16141                oDoc = oTargetWin.document;
16142            } catch (e) {
16143                oDoc = document;
16144            }
16145        }
16146
16147        var oForm = oDoc.createElement('form');
16148        oForm.method = 'POST';
16149        oForm.action = sAction;
16150
16151        // 기존 쿼리 파라미터 hidden 이관
16152        if (sQuery) {
16153            var aPairs = sQuery.split('&');
16154            for (var i = 0; i < aPairs.length; i++) {
16155                if (!aPairs[i]) { continue; }
16156                var iEq = aPairs[i].indexOf('=');
16157                var sName = iEq >= 0 ? aPairs[i].substring(0, iEq) : aPairs[i];
16158                var sVal = iEq >= 0 ? aPairs[i].substring(iEq + 1) : '';
16159                try { sVal = decodeURIComponent(sVal); } catch (e) {}
16160                CAPP_SHOP_FRONT_COMMON_UTIL._appendHiddenInput(oForm, sName, sVal);
16161            }
16162        }
16163
16164        // CA_JS 광고 폼 데이터(버튼 클릭 시점 취득, 비민감) — 예외격리
16165        try {
16166            if (typeof CA_JS !== 'undefined' && typeof CA_JS.GET_ANLYTICS_FORM_DATA === 'function') {
16167                var oFormData = CA_JS.GET_ANLYTICS_FORM_DATA();
16168                if (oFormData) {
16169                    CAPP_SHOP_FRONT_COMMON_UTIL._appendHiddenInput(oForm, 'analycis_form_data', JSON.stringify(oFormData));
16170                }
16171            }
16172        } catch (e) {}
16173
16174        if (bNewWindow === true) {
16175            var sName = 'navercheckout_' + (new Date()).getTime();
16176            window.open('', sName);
16177            oForm.target = sName;
16178        }
16179
16180        oDoc.body.appendChild(oForm);
16181        oForm.submit();
16182        if (oForm.parentNode) {
16183            oForm.parentNode.removeChild(oForm);
16184        }
16185    },
16186
16187    /**
16188     * ECHOSTING-627626 폼에 hidden input 추가 (IE 호환, 폼과 동일 document 사용)
16189     * @param {object} oForm 대상 form
16190     * @param {string} sName 필드명
16191     * @param {string} sValue 값
16192     */
16193    _appendHiddenInput: function(oForm, sName, sValue)
16194    {
16195        var oDoc = oForm.ownerDocument || document;
16196        var oInput;
16197        try {
16198            oInput = oDoc.createElement('<input name="' + sName + '">');
16199        } catch (e) {
16200            oInput = oDoc.createElement('input');
16201            oInput.setAttribute('name', sName);
16202        }
16203        oInput.setAttribute('type', 'hidden');
16204        oInput.value = sValue;
16205        oForm.appendChild(oInput);
16206    },
16207
16208    /**
16209     * [ECQAUNIT-75276] 서버 발행 알럿(몰 언어 문구)을 jet-translator 로 현재 선택 언어로 번역해 표시.
16210     *   서버 __() 는 몰 언어 기준이라 국가선택기 선택 언어와 다를 수 있다.
16211     *   엔진 미로드/현재언어==소스언어/번역 실패 시 원문 그대로 표시한다.
16212     *   (basket.js Basket._alertTranslated(ECQAINT-79580)를 전 프론트 공통으로 이동 — 담기확인창
16213     *    바로구매/관심상품 주문 등 basket.js 미로드 페이지의 주문 exec 알럿도 동일 처리)
16214     * @param {string} sMsg 알럿 원문
16215     */
16216    alertTranslated: function(sMsg)
16217    {
16218        try {
16219            var fnNorm = function(sLocale) {
16220                sLocale = (sLocale == null) ? '' : String(sLocale);
16221                var oZhMap = {'zh_CN': 'zh-CN', 'zh_TW': 'zh-TW', 'zh-CN': 'zh-CN', 'zh-TW': 'zh-TW'};
16222                if (oZhMap.hasOwnProperty(sLocale)) return oZhMap[sLocale];
16223                return sLocale.split(/[_-]/)[0];
16224            };
16225            var oJT = (typeof JetTranslator === 'undefined') ? null : JetTranslator;
16226            var oShop = (typeof CAFE24 !== 'undefined' && CAFE24.SHOP) ? CAFE24.SHOP : null;
16227            var aCookie = /(?:^|; )ec_front_language=([^;]*)/.exec(document.cookie);
16228            var sCur = fnNorm(aCookie ? decodeURIComponent(aCookie[1]) : '');
16229            var sSrc = fnNorm((oShop && typeof oShop.getLanguage === 'function') ? oShop.getLanguage() : '');
16230            if (!oJT || typeof oJT.translateTexts !== 'function' || sCur === '' || sCur === sSrc) {
16231                alert(sMsg);
16232                return;
16233            }
16234            oJT.translateTexts([sMsg], sCur).then(function(aRes) {
16235                alert((aRes || [])[0] || sMsg);
16236            }).catch(function() {
16237                alert(sMsg);
16238            });
16239        } catch (e) {
16240            // 번역 경로 실패 시 원문 표시로 폴백 — 예외는 진행 차단 없이 기록만
16241            if (typeof console !== 'undefined' && console.debug) console.debug(e);
16242            alert(sMsg);
16243        }
16244    }
16245};
16246
16247/**
16248 * console debug
16249 */
16250var EC_SHOP_FRONT_DEBUG = {
16251    bConsoleOut: false,
16252    log: function()
16253    {
16254        if (this.bConsoleOut === false) {
16255            return;
16256        }
16257        if (window.console) {
16258            console.log.apply(console, arguments);
16259        }
16260    },
16261    on: function()
16262    {
16263        this.bConsoleOut = true;
16264        localStorage.setItem('EC_SHOP_FRONT_DEBUG', '1');
16265        console.log("%c-- debug mode ON --", "color:rgb(249, 249, 249); font-size: 15px;");
16266    },
16267    off: function()
16268    {
16269        this.bConsoleOut = false;
16270        localStorage.removeItem('EC_SHOP_FRONT_DEBUG');
16271        console.log("%c-- debug mode OFF --", "color:rgb(249, 249, 249); font-size: 15px;");
16272    }
16273};
16274
16275// localStorage 기반 자동 활성화
16276if (localStorage.getItem('EC_SHOP_FRONT_DEBUG') === '1') {
16277    EC_SHOP_FRONT_DEBUG.bConsoleOut = true;
16278}
16279CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE = {
16280    /**
16281     * 로컬 스토리지 지원 여부
16282     * @return bool 지원하면 true, 지원하지 않으면 false
16283     */
16284    isSupport: function() {
16285        if (window.localStorage) {
16286            return true;
16287        } else {
16288            return false;
16289        }
16290    },
16291
16292    /**
16293     * 로컬 스토리지에 데이터 셋팅
16294     * @param string sKey 키
16295     * @param mixed mData 저장할 데이터
16296     * @param int iLifeTime 살아있는 시간(초) (기본 1일)
16297     * @return bool 정상 저장 여부
16298     */
16299    setItem: function(sKey, mData, iLifeTime) {
16300        if (this.isSupport() === false) {
16301            return false;
16302        }
16303
16304        iLifeTime = iLifeTime || 86400;
16305
16306        try {
16307            window.localStorage.setItem(sKey, JSON.stringify({
16308                iExpireTime: Math.floor(new Date().getTime() / 1000) + iLifeTime,
16309                mContent: mData
16310            }));
16311        } catch (e) {
16312            return false;
16313        }
16314
16315        return true;
16316    },
16317
16318    /**
16319     * 로컬 스토리지에서 데이터 리턴
16320     * @param string sKey 키
16321     * @return mixed 데이터
16322     */
16323    getItem: function(sKey) {
16324        if (this.isSupport() === false) {
16325            return null;
16326        }
16327
16328        var sData = window.localStorage.getItem(sKey);
16329        try {
16330            if (sData) {
16331                var aData = JSON.parse(sData);
16332                if (aData.iExpireTime > Math.floor(new Date().getTime() / 1000)) {
16333                    return aData.mContent;
16334                } else {
16335                    window.localStorage.removeItem(sKey);
16336                }
16337            }
16338        } catch (e) { }
16339
16340        return null;
16341    },
16342
16343    /**
16344     * 로컬 스토리지에서 데이터 삭제
16345     * @param string sKey 키
16346     */
16347    removeItem: function(sKey) {
16348        if (this.isSupport() === false) {
16349            return;
16350        }
16351
16352        window.localStorage.removeItem(sKey);
16353    }
16354};
16355
16356var EC_SHOP_FRONT_NEW_LIKE_BROWSER_CACHE = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_LIKE_BROWSER_CACHE');
16357
16358/**
16359 * 좋아요 관련 공통
16360 */
16361CAFE24.SHOP_FRONT_NEW_LIKE_COMMON = {
16362    CACHE_LIFE_TIME: 3600,
16363    CACHE_KEY_MY_LIKE_CATEGORY: 'localMyLikeCategoryNoList',
16364    CACHE_KEY_MY_LIKE_PRODUCT: 'localMyLikeProductNoList',
16365
16366    aConfig: {
16367        bIsUseLikeProduct: false,
16368        bIsUseLikeCategory: false
16369    },
16370
16371    init: function(aConfig) {
16372        this.aConfig = aConfig;
16373    },
16374
16375    /**
16376     * 내 분류 좋아요 번호 리스트를 가져와서 successCallbackFn 콜백 함수를 실행합니다.
16377     * @param function successCallbackFn 성공시 실행할 콜백 함수
16378     * @param function completeCallbackFn ajax 호출 완료 후 실행할 콜백 함수
16379     */
16380    getMyLikeCategoryNoInList: function(successCallbackFn, completeCallbackFn) {
16381        var self = this;
16382
16383        var aData = CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.getItem(self.CACHE_KEY_MY_LIKE_CATEGORY);
16384        if (aData !== null) {
16385            successCallbackFn(aData);
16386            if (typeof completeCallbackFn === 'function') {
16387                completeCallbackFn();
16388            }
16389        } else {
16390            EC$.ajax({
16391                url: '/exec/front/shop/LikeCommon',
16392                type: 'get',
16393                data: {
16394                    'mode': 'getMyLikeCategoryNoInList'
16395                },
16396                dataType: 'json',
16397                success: function(oReturn) {
16398                    if (oReturn.bResult === true) {
16399                        aData = oReturn.aData;
16400                        CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.setItem(self.CACHE_KEY_MY_LIKE_CATEGORY, aData, self.CACHE_LIFE_TIME);
16401                        successCallbackFn(aData);
16402                    }
16403                },
16404                complete: function() {
16405                    completeCallbackFn();
16406                }
16407            });
16408        }
16409    },
16410
16411    /**
16412     * 내 분류 좋아요 번호 리스트 캐시를 퍼지합니다.
16413     */
16414    purgeMyLikeCategoryNoInList: function() {
16415        CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.removeItem(this.CACHE_KEY_MY_LIKE_CATEGORY);
16416    },
16417
16418    /**
16419     * 내 상품 좋아요 번호 리스트를 가져와서 successCallbackFn 콜백 함수를 실행합니다.
16420     * @param function successCallbackFn 성공시 실행할 콜백 함수
16421     * @param function completeCallbackFn ajax 호출 완료 후 실행할 콜백 함수
16422     */
16423    getMyLikeProductNoInList: function(successCallbackFn, completeCallbackFn) {
16424        var self = this;
16425
16426        var aData = CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.getItem(self.CACHE_KEY_MY_LIKE_PRODUCT);
16427        if (aData !== null) {
16428            successCallbackFn(aData);
16429            if (typeof completeCallbackFn === 'function') {
16430                completeCallbackFn();
16431            }
16432        } else {
16433            EC$.ajax({
16434                url: '/exec/front/shop/LikeCommon',
16435                type: 'get',
16436                data: {
16437                    'mode': 'getMyLikeProductNoInList'
16438                },
16439                dataType: 'json',
16440                success: function(oReturn) {
16441                    if (oReturn.bResult === true) {
16442                        aData = oReturn.aData;
16443                        CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.setItem(self.CACHE_KEY_MY_LIKE_PRODUCT, aData, self.CACHE_LIFE_TIME);
16444                        successCallbackFn(aData);
16445                    }
16446                },
16447                complete: function() {
16448                    completeCallbackFn();
16449                }
16450            });
16451        }
16452    },
16453
16454    /**
16455     * 내 상품 좋아요 번호 리스트 캐시를 퍼지합니다.
16456     */
16457    purgeMyLikeProductNoInList: function() {
16458        CAFE24.SHOP_FRONT_NEW_LIKE_BROWSER_CACHE.removeItem(this.CACHE_KEY_MY_LIKE_PRODUCT);
16459    },
16460    // 숫자 관련 콤마 제거 처리(ECHOSTING-260504)
16461    getNumericRemoveCommas: function(mText) {
16462        var sSearchCommas = ',';
16463        var sReplaceEmpty = '';
16464
16465        if (EC$.inArray(typeof(mText), ['number', 'undefined']) > -1) {
16466            return mText;
16467        }
16468
16469        while (mText.indexOf(sSearchCommas) > -1) {
16470            mText = mText.replace(sSearchCommas, sReplaceEmpty);
16471        }
16472
16473        return mText;
16474    },
16475    // 숫자 관련 콤마 처리 (ECHOSTING-260504)
16476    getNumberFormat: function(iNumber) {
16477        iNumber += '';
16478
16479        var objRegExp = new RegExp('(-?[0-9]+)([0-9]{3})');
16480        while (objRegExp.test(iNumber)) {
16481            iNumber = iNumber.replace(objRegExp, '$1,$2');
16482        }
16483
16484        return iNumber;
16485    }
16486};
16487
16488var EC_SHOP_FRONT_NEW_LIKE_COMMON = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_LIKE_COMMON');
16489
16490
16491/**
16492 * 목록 > 분류 좋아요.
16493 */
16494CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY = {
16495    bIsReady: false, // 좋아요 클릭준비완료 여부.
16496    bIsSetEvent: false, // 좋아요 버튼 이벤트 지정 여부.
16497    aImgSrc: [], // 좋아요(On/Off) 아이콘 경로.
16498    aMyLikeCateNo: [], // 유저가 이미 좋아요 선택한 분류번호
16499
16500    // 분류 좋아요 초기화
16501    init: function() {
16502        // 분류 좋아요 사용안함시
16503        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.aConfig.bIsUseLikeCategory !== true) {
16504            return;
16505        }
16506
16507        // ajax 유저가 이미 좋아요 선택한 분류번호 얻기 + 아이콘세팅
16508        this.setLoadData();
16509    },
16510
16511    // 유저가 이미 좋아요 선택한 분류번호 얻기 + 아이콘세팅
16512    setLoadData: function() {
16513        if (EC$('.likeCateIcon').count < 1) {
16514            return;
16515        }
16516
16517        var self = this;
16518
16519        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getMyLikeCategoryNoInList(function(aData) {
16520            self.aImgSrc = aData.imgSrc;
16521            self.aMyLikeCateNo = aData.rows;
16522
16523            // 아이콘(on) 세팅
16524            self.setMyLikeCategoryIconOn();
16525
16526            // 좋아요 클릭 이벤트핸들러 지정
16527            if (self.bIsSetEvent === false) {
16528                self.setEventHandler();
16529                self.bIsSetEvent = true;
16530            }
16531        }, function() {
16532            self.bIsReady = true;
16533        });
16534    },
16535
16536    // 페이지 로드시 유저가 좋아요한 분류 On.아이콘으로 변경
16537    setMyLikeCategoryIconOn: function() {
16538        var aData = this.aMyLikeCateNo;
16539
16540        for (var i=0; i < aData.length; i++) {
16541            // selected 스타일 적용
16542            EC$(".likeCate_" + aData[i].category_no).each(function() {
16543                EC$(this).addClass('selected');
16544            });
16545
16546            // 아이콘 이미지경로 변경
16547            EC$(".likeCateIcon[category_no='" + aData[i].category_no + "']").each(function() {
16548                EC$(this).attr({'src': CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.aImgSrc.on, 'icon_status': 'on'});
16549            });
16550        }
16551    },
16552
16553    setEventHandler: function() {
16554        try {
16555            EC$(document).on('click', '.likeCate', CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.clickLikeIcon);
16556        } catch (e) {}
16557    },
16558
16559    clickLikeIcon: function() {
16560        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.bIsReady === false) {
16561            return;
16562        }
16563
16564        // 클릭한 분류 좋아요수 + 아이콘 정보 얻기
16565        var iCateNo = EC$('.likeCateIcon', this).attr('category_no');
16566        var sIconStatus= EC$('.likeCateIcon', this).attr('icon_status');
16567        // 카운트 string > int 형으로 변환 (ECHOSTING-260504)
16568        var iLikeCount = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getNumericRemoveCommas(EC$('.likeCateCount', this).text());
16569
16570        // 아이콘경로 및 좋아요수 증감처리
16571        var sNewImgSrc = sNewIconStatus = "";
16572        var iNewLikeCount = 0;
16573        var oLikeWrapNode = EC$(".likeCate_" + iCateNo);
16574
16575        if (sIconStatus === 'on') {
16576            sNewIconStatus = 'off';
16577            sNewImgSrc = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.aImgSrc.off;
16578            if (iLikeCount > 0) {
16579                iNewLikeCount = --iLikeCount;
16580            }
16581
16582            oLikeWrapNode.each(function() {
16583                EC$(this).removeClass('selected');
16584            });
16585        } else {
16586            sNewIconStatus = 'on';
16587            sNewImgSrc = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.aImgSrc.on;
16588            iNewLikeCount = ++iLikeCount;
16589
16590            // 동일상품 selected 스타일적용
16591            oLikeWrapNode.each(function() {
16592                EC$(this).addClass('selected');
16593            });
16594        }
16595
16596        // 좋아요 카운트 number_format (ECHOSTING-260504)
16597        iNewLikeCount = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getNumberFormat(iNewLikeCount);
16598
16599        // 동일분류 아이콘 변경
16600        EC$(".likeCateIcon[category_no='" + iCateNo + "']").each(function() {
16601            EC$(this).attr({'src': sNewImgSrc, 'icon_status': sNewIconStatus});
16602        });
16603
16604        // 동일분류 좋아요수 변경
16605        EC$('.likeCateCount_' + iCateNo).each(function() {
16606            EC$(this).text(iNewLikeCount);
16607        });
16608
16609        // ajax 호출 좋아요수(상품) + 마이쇼핑 좋아요 저장
16610        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.submitMyLikeCategory(iCateNo, sNewIconStatus);
16611    },
16612
16613    // 분류 좋아요수 + 마이쇼핑 좋아요 저장
16614    submitMyLikeCategory: function(iCateNo, sIconStatus) {
16615        if (sIconStatus === 'on') {
16616            this.aMyLikeCateNo.push(iCateNo);
16617        } else {
16618            this.aMyLikeCateNo.pop(iCateNo);
16619        }
16620
16621        EC$.ajax({
16622            url: '/exec/front/shop/LikeCommon',
16623            type: 'get',
16624            data: {
16625                'mode': 'saveMyLikeCategory',
16626                'iCateNo': iCateNo,
16627                'sIconStatus': sIconStatus
16628            },
16629            dataType: 'json',
16630            success: function(oReturn) {
16631                if (oReturn.bResult === true) {
16632                    CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.purgeMyLikeCategoryNoInList();
16633                }
16634            },
16635            complete: function() {
16636                CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.bIsReady = true;
16637            }
16638        });
16639    }
16640};
16641
16642var EC_SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY');
16643
16644EC$(function() {
16645    CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_CATEGORY.init(); // 분류 좋아요.
16646});
16647
16648CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT = {
16649    aProductNo: [], bIsReviewTalk: 'F', setReviewTalkCnt: function () {
16650        var bIsUse = this.checkUseReviewTalk();
16651
16652        if (bIsUse === true) {
16653            this.setDataProductNo();
16654            this.setResponseCountData();
16655        }
16656    },
16657
16658    checkUseReviewTalk: function () {
16659        return (this.bIsReviewTalk === 'T' && EC$('.reviewtalk_review_count').length > 0) ? true : false;
16660    },
16661
16662    setDataProductNo: function () {
16663        var aAllProductNo = [];
16664        EC$('.reviewtalk_review_count').each(function () {
16665            aAllProductNo.push(EC$(this).attr('data-product-no'));
16666        });
16667
16668        CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT.aProductNo = EC$.uniqueSort(aAllProductNo);
16669    },
16670
16671    setResponseCountData: function () {
16672        if (this.aProductNo.length < 1) {
16673            return;
16674        }
16675
16676        EC$.ajax({
16677            url: '/exec/front/shop/ApiReviewtalkReviewcnt', type: 'get', data: {
16678                'product_no': this.aProductNo.join('_')
16679            }, dataType: 'json', success: function (oResponse) {
16680                if (oResponse.result === true) {
16681                    CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT.setResponseData(oResponse.data);
16682                }
16683            }
16684        });
16685    },
16686
16687    //천단위 콤마 표시
16688    number_format: function(str)
16689    {
16690        // 3자리씩 ,로 끊어서 리턴
16691        str = String(parseInt(str));
16692        var regexp = /^(-?[0-9]+)([0-9]{3})($|\.|,)/;
16693        while (regexp.test(str)) {
16694            str = str.replace(regexp, "$1,$2$3");
16695        }
16696        return str;
16697    },
16698
16699    setResponseData: function (oResponseData) {
16700        var oProductReviewCnt = oResponseData;
16701
16702        if (this.checkUseReviewTalk() === true) {
16703            EC$('.reviewtalk_review_count').each(function () {
16704                var iProductNo = EC$(this).attr('data-product-no');
16705                var sFormat = EC$(this).attr('data-format');
16706                var iReviewCount = 0;
16707
16708                if (oProductReviewCnt.hasOwnProperty(iProductNo) === true && oProductReviewCnt[iProductNo].hasOwnProperty('review_count') === true) {
16709                    iReviewCount = oProductReviewCnt[iProductNo].review_count;
16710                }
16711
16712                EC$(this).text(sFormat.replace('REVIEWTALKCOUNT', CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT.number_format(iReviewCount)));
16713
16714                var sAddClass = 'reviewtalk_count_display_' + iReviewCount;
16715                EC$(this).parent().addClass(sAddClass);
16716                EC$(this).parent().siblings('.title').addClass(sAddClass);
16717            });
16718        }
16719    }
16720};
16721
16722var EC_SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT');
16723
16724EC$(function () {
16725    CAFE24.SHOP_FRONT_REVIEW_TALK_REVIEW_COUNT.setReviewTalkCnt();
16726});
16727
16728
16729EC$(function() {
16730    ReferenceCurrencyPrice.init();
16731});
16732
16733var ReferenceCurrencyPrice = {
16734    sOriginReferCurrency : null,
16735    sReferCurrency : null,
16736    aCurrencyRate : null,
16737    aCurrencyInfo : null,
16738    sRefPrice : 'eRefPrice',
16739    sRefPriceUnitHead : 'eRefPriceUnitHead',
16740    sRefPriceUnitTail : 'eRefPriceUnitTail',
16741
16742    init : function () {
16743        var self = this;
16744        if (CAFE24.CURRENCY_INFO.isUseReferenceCurrency() !== true) { return; }
16745        this.getReferCurrency();
16746
16747        if (this.sOriginReferCurrency !== this.sReferCurrency) {
16748            this.setShopPrice();
16749            this.setCurrencyUnit();
16750        }
16751
16752    },
16753    getReferCurrency : function () {
16754        var self = this;
16755        this.sOriginReferCurrency = CAFE24.CURRENCY_INFO.getOriginReferenceCurrency();
16756        this.sReferCurrency = EC$.cookie('EC_REFER_CURRENCY_' + CAFE24.SDE_SHOP_NUM);
16757
16758        var CURRENCY_RATE = localStorage.getItem('EC_CURRENCY_RATE');
16759        this.aCurrencyRate = JSON.parse(CURRENCY_RATE);
16760        this.aCurrencyInfo = CAFE24.CURRENCY_INFO.getCurrencyList(this.sReferCurrency);
16761    },
16762    setShopPrice : function () {
16763        var self = this;
16764        var rate = CAFE24.SHOP_PRICE_FORMAT.detachFormat(ReferenceCurrencyPrice.aCurrencyRate[ReferenceCurrencyPrice.sOriginReferCurrency]["rates"][ReferenceCurrencyPrice.sReferCurrency]);
16765
16766        EC$('.' + ReferenceCurrencyPrice.sRefPrice).each(function() {
16767            if (EC$(this).attr('changed') === 'changed') { return; }
16768            if (rate == 0) {
16769                var fRefPrice = 0;
16770            } else {
16771                var fOriginRefPrice = CAFE24.SHOP_PRICE_FORMAT.detachFormat(EC$(this).text());
16772                var fRefPrice = fOriginRefPrice / rate;
16773            }
16774
16775            EC$(this).text(CAFE24.SHOP_PRICE.toPrice(fRefPrice, ReferenceCurrencyPrice.aCurrencyInfo));
16776            EC$(this).attr('changed', 'changed');
16777        });
16778
16779    },
16780    setCurrencyUnit : function () {
16781        var self = this;
16782
16783        //화폐단위
16784        var UnitFormat = this.aCurrencyInfo['currency_symbol'];
16785        //통화단위
16786        if (CAFE24.SHOP_CURRENCY_INFO[CAFE24.SDE_SHOP_NUM].aShopSubCurrencyInfo.display_type === 'M') {
16787            UnitFormat = this.sReferCurrency;
16788        }
16789        this.setUnitHead(UnitFormat);
16790        this.setUnitTail();
16791
16792    },
16793    setUnitHead : function (UnitFormat) {
16794        var self = this;
16795        EC$('.' + this.sRefPriceUnitHead).each(function() {
16796            if (EC$(this).attr('changed') === 'changed') { return; }
16797
16798            EC$(this).html(EC$(this).text().replace(/[^(\s]+/, UnitFormat));
16799            EC$(this).attr('changed', 'changed');
16800        });
16801
16802    },
16803    setUnitTail : function () {
16804        var self = this;
16805        EC$('.' + this.sRefPriceUnitTail).each(function() {
16806            if (EC$(this).attr('changed') === 'changed') { return; }
16807
16808            EC$(this).text(EC$(this).text().replace(/[^)\s]+/, ''));
16809            EC$(this).attr('changed', 'changed');
16810        });
16811
16812    }
16813
16814};
16815var categoryOddColor = new Object();
16816var categoryEvenColor = new Object();
16817
16818EC$(function()
16819{
16820    // 카테고리타입
16821    var aCategoryType = new Array('normal', 'reco', 'new', 'project', 'main');
16822    // 상품 ID prefix
16823    var sProductIdPrefix = 'product_';
16824    // 옵션 미리보기 아이콘 ID prefix
16825    var sOptPreviewIconId = 'opt_prv_id_';
16826    // 옵션 미리보기 레이어 ID prefix
16827    var sOptPreviewLayerId = 'opt_prv_layer_id_';
16828    // 옵션 미리보기 닫기 버튼 ID prefix
16829    var sOptPreviewCloseId = 'opt_prv_close_id_';
16830
16831    // 상품요약정보 (툴팁)
16832    if (EC$('.tooltip').length > 0 && EC$.fn.Tooltip) {
16833        EC$('.tooltip').Tooltip({
16834            'name': 'toolTipStyle',
16835            'delay': '0',
16836            'top': '-200',
16837            'left': '10',
16838            'fade': false,
16839            'opacity': 1
16840        });
16841    }
16842
16843    /**
16844     * 카테고리 타입별로 홀짝수 라인색상 설정
16845     */
16846    var iCategoryTypeLen = aCategoryType.length;
16847    for (var i = 0; i < iCategoryTypeLen; i++) {
16848        var iBeforeOffsetTop = -1;
16849        var sCategoryType = aCategoryType[i];
16850        var sBgColor = categoryOddColor[sCategoryType];
16851        EC$('[id^="' + sProductIdPrefix + aCategoryType[i] + '_"]').each(function(idx)
16852        {
16853            if ((idx > 0) && EC$(this).attr('offsetTop') != iBeforeOffsetTop) {
16854                sBgColor = (sBgColor == categoryOddColor[sCategoryType]) ? categoryEvenColor[sCategoryType] : categoryOddColor[sCategoryType];
16855            }
16856            iBeforeOffsetTop = EC$(this).attr('offsetTop');
16857            EC$(this).css('background-color',sBgColor);
16858        });
16859    }
16860
16861    EC$('#selArray').change(function() {
16862        location.href = EC$(this).val();
16863    });
16864
16865    var sSortName = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('sort_method');
16866
16867    if (sSortName !== '') {
16868
16869        if (sSortName.indexOf('#Product_ListMenu') < 0) {
16870            sSortName = sSortName + '#Product_ListMenu';
16871        }
16872
16873        EC$('#selArray>option').each(function() {
16874            if (EC$(this).val().indexOf(sSortName) > 0) {
16875                EC$(this).prop('selected', true);
16876            }
16877        });
16878    }
16879
16880
16881    /**
16882     * 옵션아이콘 onmouseover 핸들러
16883     */
16884    EC$('[id^="' + sOptPreviewIconId + '"]').mouseover(function()
16885    {
16886        if (sOptionPreviewMethod.indexOf('mouseover') > -1)
16887            setOptLayerDisplay(EC$(this));
16888    });
16889
16890    /**
16891     * 옵션아이콘 onmouseclick 핸들러
16892     */
16893    EC$('[id^="' + sOptPreviewIconId + '"]').click(function()
16894    {
16895        if (sOptionPreviewMethod.indexOf('mouseclick') > -1)
16896            setOptLayerDisplay(EC$(this));
16897    });
16898
16899    /**
16900     * 옵션 하나만 선택가능 옵션 동작
16901     */
16902    try {
16903        EC$(document).on('click', 'input[name="item_code[]"]', function() {
16904            if (EC$.data(document, 'sUseOptionOne_class') === 'T') {
16905                if (EC$('input[name="item_code[]"][option_name="' + EC$(this).attr('option_name') + '"]:checked').length > 1) {
16906                    alert(__('옵션별로 1개 씩만 선택 가능한 상품입니다.'));
16907                    EC$(this).prop('checked', false);
16908                }
16909
16910            }
16911        });
16912    } catch (e) {}
16913    /**
16914     * 옵션 레이어 display 조절
16915     *
16916     * @param object optIcon 옵션 아이콘 JQuery 객체
16917     * @param string sPopupMethod 팝업 method (mouseover|mouseclick)
16918     */
16919    function setOptLayerDisplay(optIcon, sPopupMethod)
16920    {
16921        var aParam = getOptionParams(optIcon.attr('id'),sOptPreviewIconId);
16922        // 모든 옵션미리보기창 닫기
16923        EC$('[id^="' + sOptPreviewLayerId + '"]').each(function()
16924        {
16925            EC$(this).css('display','none');
16926        });
16927
16928        // 선택된 옵션미리보기창 출력
16929        var sLayerId = '#' + sOptPreviewLayerId + aParam['product_no'];
16930        var aPos = findPos(optIcon.get(0));
16931        EC$(sLayerId).css('position','absolute');
16932        EC$(sLayerId).css('left',aPos['left']);
16933        EC$(sLayerId).css('top',(aPos['top'] + optIcon.attr('offsetHeight')) + 'px');
16934        EC$(sLayerId).css('display','');
16935        EC$(sLayerId).css('z-index','9999');
16936    }
16937
16938    /**
16939     * 옵션아이콘 onmouseout 핸들러
16940     */
16941    EC$('[id^="' + sOptPreviewIconId + '"]').mouseout(function()
16942    {
16943        var aParam = getOptionParams(EC$(this).attr('id'),sOptPreviewIconId);
16944        if (sOptionLayerCloseMethod != 'use_close_button')
16945            EC$('#' + sOptPreviewLayerId + aParam['product_no']).css('display','none');
16946    });
16947
16948    /**
16949     * 옵션 레이어 onmouseover 핸들러
16950     */
16951    EC$('[id^="' + sOptPreviewLayerId + '"]').mouseover(function()
16952    {
16953        EC$(this).css('display','');
16954    });
16955
16956    /**
16957     * 옵션 레이어 onmouseout 핸들러
16958     */
16959    EC$('[id^="' + sOptPreviewLayerId + '"]').mouseout(function()
16960    {
16961        if (sOptionLayerCloseMethod != 'use_close_button')
16962            EC$(this).css('display','none');
16963    });
16964
16965    /**
16966     * 옵션 레이어 닫기버튼 클릭 핸들러
16967     */
16968    EC$('[id^="' + sOptPreviewCloseId + '"]').click(function()
16969    {
16970        var aParam = getOptionParams(EC$(this).attr('id'),sOptPreviewCloseId);
16971        EC$('#' + sOptPreviewLayerId + aParam['product_no']).css('display','none');
16972    });
16973
16974    /**
16975     * HTML 오브젝트의 위치값 계산
16976     *
16977     * @param object obj 위치를 알고자 하는 오브젝트
16978     * @return object left, top 값
16979     */
16980    function findPos(obj)
16981    {
16982        var iCurLeft = iCurTop = 0;
16983
16984        if (obj.offsetParent) {
16985            do {
16986                iCurLeft += obj.offsetLeft;
16987                iCurTop += obj.offsetTop;
16988            } while (obj = obj.offsetParent);
16989        }
16990
16991        return {
16992            'left': iCurLeft,
16993            'top': iCurTop
16994        };
16995    }
16996
16997    /**
16998     * 옵션관련 ID를 파싱해서 파라메터 추출, 반환
16999     *
17000     * @param string sId ID
17001     * @param string sPrefix 파싱할 때 삭제할 prefix
17002     * @return array 상품번호+팝업method
17003     */
17004    function getOptionParams(sId, sPrefix)
17005    {
17006        var aTmp = sId.replace(sPrefix,'').split('_');
17007        return {
17008            'product_no': aTmp[0],
17009            'popup_method': aTmp[1]
17010        };
17011    }
17012
17013    // 할인기간 레이어 열기
17014    EC$('.shippingFee').on('click', '.deliveryBenefitDetailInfo', function() {
17015        var oLayerDiscountDelivery = EC$(this).parent();
17016
17017        if (oLayerDiscountDelivery.attr('benefitinfoLoaded') === undefined) {
17018
17019            var iProductNo = EC$(this).attr("productno");
17020            var sBenefitType = EC$(this).attr("benefit_type");
17021
17022            var oHtml = EC$('<div>');
17023            oHtml.addClass('ec-base-tooltip');
17024            oHtml.addClass('wrap');
17025            EC$(this).parent().append(oHtml);
17026
17027            EC$.post('/exec/front/Product/Benefitinfo', 'benefit_type='+sBenefitType+'&product_no=' + iProductNo, function(sHtml) {
17028                oHtml.html(sHtml);
17029                oLayerDiscountDelivery.attr('benefitinfoLoaded', true);
17030            });
17031        }
17032
17033        oLayerDiscountDelivery.find('div.ec-base-tooltip').show();
17034        oLayerDiscountDelivery.find('span.arrow').show();
17035
17036        return false;
17037    });
17038
17039
17040    // 할인기간 레이어 열기
17041    EC$('.discountPeriod > a').mouseover(function() {
17042        EC$('.layerDiscountPeriod').hide();
17043        EC$(this).parent().find('.layerDiscountPeriod').show();
17044    }).mouseout(function() {
17045        EC$('.layerDiscountPeriod').hide();
17046    });
17047
17048    // 차등 배송비 사용시 ToolTip 열기
17049    try {
17050        EC$(document).on('click', '.btnTooltip > a', function() {
17051           EC$('.btnTooltip > .differentialShipping').show();
17052        });
17053    } catch (e) {}
17054    // 차등 배송비 사용시 ToolTip 닫기
17055    EC$('.btnTooltip > .differentialShipping a').off().click(function() {
17056        EC$('.btnTooltip > .differentialShipping').hide();
17057    });
17058
17059
17060    // 배송비정보 레이어 닫기
17061    try {
17062        EC$(document).on('click', '.ec-base-tooltip > .close', function() {
17063            EC$(this).parents('.ec-base-tooltip').hide();
17064            EC$(this).parents('.differentialShipping').hide();
17065            return false;
17066        });
17067    } catch (e) {}
17068
17069    // 배송비정보 레이어 닫기
17070    try {
17071        EC$(document).on('click', '.differentialShipping > .close > a', function() {
17072            EC$(this).parents('.differentialShipping').hide();
17073        });
17074    } catch (e) {}
17075
17076    COLORCHIPLIST.init();
17077
17078});
17079
17080//컬러칩 이미지 변경(상품리스트)
17081var COLORCHIPLIST = {
17082    init: function() {
17083        var sSelector = '';
17084        if (mobileWeb === false) {
17085            sSelector = 'div.color > .chips';
17086        } else {
17087            sSelector = '.xans-product-colorchip .chips';
17088        }
17089        try {
17090            EC$(document).on('mouseover click touchstart', sSelector, function() {
17091                var iColorNo = EC$(this).attr('color_no');
17092                var iDisplayGroup = EC$(this).attr('displayGroup');
17093
17094                if (iColorNo != '') {
17095                    EC$(this).css('cursor', 'pointer');
17096                    COLORCHIPLIST.getImage(this, iColorNo, iDisplayGroup);
17097                }
17098            });
17099        } catch (e) {}
17100    },
17101
17102    getImage: function(oObj, iColorNo, iDisplayGroup) {
17103        var sImageUrl = EC$.data(EC$(oObj)[0], 'image');
17104
17105        if (sImageUrl == undefined) {
17106            COLORCHIPLIST.getAjax(oObj, iColorNo, iDisplayGroup);
17107        } else {
17108            COLORCHIPLIST.setDisplayImage(oObj);
17109        }
17110    },
17111
17112    getAjax: function(oObj, iColorNo, iDisplayGroup)
17113    {
17114        EC$.get(
17115            '/exec/front/Product/Colorimage',
17116            'iColorNo=' + iColorNo + '&iDisplayGroup=' + iDisplayGroup,
17117            function(sResponse) {
17118                if (sResponse != '') {
17119                    var oJson = CAFE24.UTIL.parseJSON(sResponse);
17120                    EC$.data(EC$(oObj)[0], 'image', oJson.sImageUrl);
17121                    EC$.data(EC$(oObj)[0], 'displayGroup', oJson.iDisplayGroup);
17122                    EC$.data(EC$(oObj)[0], 'product_no', oJson.iProductNo);
17123                    COLORCHIPLIST.setDisplayImage(oObj);
17124                }
17125            }
17126        );
17127    },
17128
17129    setDisplayImage: function(oObj)
17130    {
17131        var iDisplayGroup = EC$.data(EC$(oObj)[0], 'displayGroup');
17132        if (iDisplayGroup === 0) {
17133            iDisplayGroup = '';
17134        }
17135
17136        var iProductNo = EC$.data(EC$(oObj)[0], 'product_no');
17137        var sImageUrl = EC$.data(EC$(oObj)[0], 'image');
17138
17139        var oEl = EC$('#eListPrdImage' + iProductNo + '_' + iDisplayGroup);
17140        oEl.attr('src', sImageUrl);
17141
17142
17143    }
17144};
17145
17146// 상품 확대보기 아이콘 ID prefix
17147var sProductZoomIdPrefix = 'product_zoom_';
17148
17149/**
17150 * 상품 확대보기
17151 *
17152 * @param int iProductNo 상품번호
17153 * @param int iCategoryNo 카테고리 번호
17154 * @param int iDisplayGroup display_group
17155 * @param string sLink 팝업창 URL
17156 * @param string sOption 팝업 옵션
17157 */
17158function zoom(iProductNo, iCategoryNo, iDisplayGroup, sLink, sOption)
17159{
17160    // 팝업창 링크
17161    var sLink = sLink ? sLink : '/product/image_zoom.html';
17162    sLink += '?product_no=' + iProductNo + '&cate_no=' + iCategoryNo + '&display_group=' + iDisplayGroup;
17163    // 팝업창 옵션
17164    var sOptions = sOption ? sOption : 'toolbar=no,scrollbars=no,resizable=yes,width=800,height=640,left=0,top=0';
17165    // 팝업창 이름
17166    var sWinName = 'image_zoom';
17167
17168    window.open(sLink,sWinName,sOptions);
17169}
17170
17171/**
17172 * 상품상세 확대보기
17173 *
17174 * @param int iProductNo 상품번호
17175 * @param int iCategoryNo 카테고리 번호
17176 * @param int iDisplayGroup display_group
17177 * @param string sLink 팝업창 URL
17178 * @param string sOption 팝업 옵션
17179 */
17180function zoom2(iProductNo, iCategoryNo, iDisplayGroup, sLink, sOption)
17181{
17182    // 팝업창 링크
17183    var sLink = sLink ? sLink : '/product/image_zoom2.html';
17184    sLink += '?product_no=' + iProductNo + '&cate_no=' + iCategoryNo + '&display_group=' + iDisplayGroup;
17185    // 팝업창 옵션
17186    var sOptions = sOption ? sOption : 'toolbar=no,scrollbars=no,resizable=yes,width=800,height=640,left=0,top=0';
17187    // 팝업창 이름
17188    var sWinName = 'image_zoom2';
17189
17190    window.open(sLink,sWinName,sOptions);
17191}
17192
17193/**
17194 * 상품 진열시 높이가 달라서 li가 깨지는 현상이 나타날때 이를 진열된 상품의 기준으로 높이를 다시 재설정해주는 스크립트입니다.
17195 * 이 스크립트는 반드시 고정폭에서 사용되어야 합니다.
17196 * 해당스크립트 실행문은 각각 모듈의 js에서 합니다.
17197 */
17198$.fn.productResize = function(nodeName) {
17199    nodeName = nodeName || 'li';
17200
17201    return $(this).each(function() {
17202        var iTargetHeight = 0;
17203        var aTargetElement = new Array();
17204        var nodes = $(this).find(nodeName);
17205        var iFirstChildDepth = $(nodes[0]).parents().length; // 타겟 depth
17206        for (var x = 0; x < nodes.length; x++) {
17207            if ($(nodes[x]).parents().length == iFirstChildDepth) {
17208                aTargetElement.push(x);
17209                if (iTargetHeight < $(nodes[x]).height()) {
17210                    iTargetHeight = $(nodes[x]).height();
17211                }
17212            }
17213        }
17214        for (var x in aTargetElement) {
17215            $(nodes[aTargetElement[x]]).height(iTargetHeight);
17216        }
17217    });
17218};
17219EC$.fn.productResize = function(nodeName) {
17220    nodeName = nodeName || 'li';
17221
17222    return EC$(this).each(function() {
17223        var iTargetHeight = 0;
17224        var aTargetElement = new Array();
17225        var nodes = EC$(this).find(nodeName);
17226        var iFirstChildDepth = EC$(nodes[0]).parents().length; // 타겟 depth
17227        for (var x = 0; x < nodes.length; x++) {
17228            if (EC$(nodes[x]).parents().length == iFirstChildDepth) {
17229                aTargetElement.push(x);
17230                if (iTargetHeight < EC$(nodes[x]).height()) {
17231                    iTargetHeight = EC$(nodes[x]).height();
17232                }
17233            }
17234        }
17235        for (var x in aTargetElement) {
17236            EC$(nodes[aTargetElement[x]]).height(iTargetHeight);
17237        }
17238    });
17239};
17240/**
17241 * 상품 리스트에서 쓰이는 기능 모음 1. 옵션 미리보기 2. 장바구니 넣기 3. 이미지 줌 4. 요약정보
17242 */
17243CAFE24.LIST_ACTION = {
17244    getOptionSelect: function(iProductNo, iCategoryNo, iDisplayGroup, sBasketType)
17245    {
17246        element = document;
17247        EC$('div.xans-product-basketoption').remove();
17248        EC$.get(basket_option,{
17249            'product_no': iProductNo,
17250            'cate_no': iCategoryNo,
17251            'display_group': iDisplayGroup,
17252            'basket_type': sBasketType
17253        },function(sHtml)
17254        {
17255            EC$('body').append(EC$(sHtml.replace(/[<]script( [^ ]+)? src=\"[^>]*>([\s\S]*?)[<]\/script>/g,"")));
17256        });
17257    },
17258    getOptionSelectValidate: function(sType)
17259    {
17260        var iCheckCount = 0;
17261        var bReturn = true;
17262        var bFirst = true;
17263        var eLists = EC$('.xans-product-optionlist');
17264        var iProductMin = parseInt(EC$.data(document,'ProductMin_class'),10);
17265
17266        // 뉴상품인 경우에만 있는 데이터
17267        var iProductMax = parseInt(EC$.data(document,'ProductMax_class'),10);
17268        var iBuyUnit = parseInt(EC$.data(document,'ProductBuyUnit_class'),10);
17269
17270        if (isNaN(iBuyUnit) === true) iBuyUnit = 1;
17271        if (isNaN(iProductMax) === true) iProductMax = 0;
17272
17273        if (EC$.data(document,'BundlePromotion') === true) {
17274            iBuyUnit = 1;
17275            iProductMin = 1;
17276            iProductMax = 0;
17277        }
17278
17279        var sOptionType = EC$.data(document, 'sOptionType_class');
17280        var aOptionName = CAFE24.UTIL.parseJSON(EC$.data(document, 'aOptionName_class'));
17281        if (sOptionType === 'F') {
17282            EC$(aOptionName).each(function(i) {
17283                if (EC$('input[option_name="'+aOptionName[i]+'"]:checked').length == 0 && EC$('input[option_name="'+aOptionName[i]+'"]').attr('require') === 'T') {
17284                    alert(__('필수옵션은 반드시 1개 이상 선택하셔야 구매 또는 장바구니 담기가 가능합니다.'));
17285                    eOptionName.focus();
17286                    bReturn = false;
17287                    bFirst = false;
17288                    return false;
17289                }
17290            });
17291            if (bReturn === false) {
17292                bFirst = false;
17293                return false;
17294            }
17295        }
17296
17297        var aQuantity = new Array();
17298        for (var x = 0; x < eLists.length; x++) {
17299            var eList = EC$(eLists[x]);
17300            eList.find('.' + EC$.data(document,'Check_class')).each(function() {
17301                if (EC$(this).prop('checked') === true) {
17302                    iCheckCount++;
17303                    eList.find('.' + EC$.data(document,'Quantity_class')).each(function() {
17304                        var iQuantity = parseInt(EC$(this).val(), 10);
17305                        aQuantity.push(iQuantity);
17306                        if (bFirst === true) {
17307                            if (iQuantity < 1) {
17308                                alert(__('구매하실 수량을 입력해주세요'));
17309                                EC$(this).focus();
17310                                bReturn = false;
17311                                return false;
17312                            }
17313
17314                            if ((EC$(this).attr('stock') > 0 || EC$(this).attr('is_soldout') === 'T') && iQuantity > EC$(this).attr('stock')) {
17315                                alert(__('선택하신 옵션에 해당하는 상품의 재고 수량이 구매하실 수량보다 적습니다.'));
17316                                EC$(this).focus();
17317                                bReturn = false;
17318                                return false;
17319                            }
17320
17321                            if (iQuantity % iBuyUnit !== 0) {
17322                                alert(sprintf(__('선택하신 상품은 %s개 단위로 구매 하실 수 있습니다.'), iBuyUnit));
17323                                EC$(this).focus();
17324                                bReturn = false;
17325                                return false;
17326                            }
17327
17328                            if (iQuantity < iProductMin) {
17329                                alert(sprintf(__('최소 주문수량은 %s개 입니다.'), iProductMin));
17330                                EC$(this).focus();
17331                                bReturn = false;
17332                                return false;
17333                            }
17334                            if (iProductMax > 1 && iQuantity > iProductMax) {
17335                                alert(sprintf(__('최대 주문수량은 %s개 입니다.'), iProductMax));
17336                                EC$(this).focus();
17337                                bReturn = false;
17338                                return false;
17339                            }
17340                        }
17341
17342                        if (bReturn === false) {
17343                            bFirst = false;
17344                        }
17345                    });
17346                    if (bReturn === false) {
17347                        bFirst = false;
17348                    }
17349                }
17350            });
17351        }
17352        if (iCheckCount < 1) {
17353            alert(__('구매 또는 장바구니에 담을 상품을 선택해주세요.'));
17354            return false;
17355        }
17356        if (bReturn === true) {
17357            if (typeof CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE === 'object') {
17358                var iProductNum = EC$.data(document,'iProductNo_class');
17359                if (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.oBundleConfig.hasOwnProperty(iProductNum) === true) {
17360                    if (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.oBundleConfig[iProductNum] !== null) {
17361                        if (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.isValidQuantity(aQuantity, iProductNum) === false) {
17362                            return false;
17363                        }
17364                    }
17365                }
17366            }
17367
17368            this.setBasketPrepare(sType);
17369        } else {
17370            return false;
17371        }
17372    },
17373    setBasketPrepare: function(sType)
17374    {
17375        var frm = this.getBasketForm();
17376        this.getHiddenElement('product_no',EC$.data(document,'iProductNo_class')).appendTo(frm);
17377        this.getHiddenElement('main_cate_no',EC$.data(document,'iCategoryNo_class')).appendTo(frm);
17378        this.getHiddenElement('display_group',EC$.data(document,'iDisplayGroup_class')).appendTo(frm);
17379        this.getHiddenElement('basket_type',EC$.data(document,'sBasketType_class')).appendTo(frm);
17380        this.getHiddenElement('product_min',EC$.data(document,'ProductMin_class')).appendTo(frm);
17381        this.getHiddenElement('delvtype',EC$('input[name="delvtype"]').val()).appendTo(frm);
17382        this.getHiddenElement('option_type','T').appendTo(frm);
17383        this.getHiddenElement('command','add').appendTo(frm);
17384        this.getHiddenElement('has_option','T').appendTo(frm);
17385        var eLists = EC$('.xans-product-optionlist');
17386        var bAddProduct = false;
17387        var sOptionParam = '';
17388        for (var x = 0; x < eLists.length; x++) {
17389            var eList = EC$(eLists[x]);
17390            eList.find('.' + EC$.data(document,'Check_class') + ':checked').each(function()
17391            {
17392                var sOptionId = EC$(this).val();
17393                var iQuantity = eList.find('.' + EC$.data(document,'Quantity_class')).val();
17394                if (bAddProduct === false) {
17395                    var aOption = sOptionId.split('-');
17396                    var k = 0;
17397                    for (var z = 0; z < aOption.length; z++) {
17398                        key = z + 1;
17399                        EC_ListAction.getHiddenElement('option' + key,aOption[z]).appendTo(frm);
17400                    }
17401
17402                    eList.find('.' + EC$.data(document,'Quantity_class')).each(function()
17403                    {
17404                        EC_ListAction.getHiddenElement('quantity',iQuantity).appendTo(frm);
17405                        bAddProduct = true;
17406                    });
17407                } else {
17408                    var aBasketInfo = new Array();
17409                    aBasketInfo.push(EC$.data(document,'iProductNo_class'));
17410                    aBasketInfo.push(EC$.data(document,'iCategoryNo_class'));
17411                    aBasketInfo.push(EC$.data(document,'iDisplayGroup_class'));
17412                    aBasketInfo.push(EC$.data(document,'ProductMin_class'));
17413                    aBasketInfo.push('product_name');
17414                    aBasketInfo.push('product_price');
17415                    aBasketInfo.push('T');
17416                    aBasketInfo.push(iQuantity);
17417                    aBasketInfo.push(EC$.data(document,'iOptionSize_class'));
17418                    var aOption = sOptionId.split('-');
17419                    var k = 0;
17420                    for (var z = 0; z < aOption.length; z++) {
17421                        if (aOption[z] != '0') {
17422                            aBasketInfo.push(aOption[z]);
17423                        }
17424                    }
17425                    EC_ListAction.getHiddenElement('basket_info[]',aBasketInfo.join('|')).appendTo(frm);
17426                }
17427
17428                if (iQuantity > 0) {
17429                    frm.append(getInputHidden('selected_item[]',iQuantity+'||'+sOptionId));
17430                }
17431            });
17432        }
17433        // 선택한상품만 주문하기
17434        if (sType == 1 || sType == 'naver_checkout' || sType == 'checkout') {
17435            // 이미 장바구니에 들어있는지 체크
17436            this.selectbuy_action(EC$.data(document,'iProductNo_class'));
17437            EC_ListAction.getHiddenElement('quantity_override_flag', sIsPrdOverride).appendTo(frm);
17438        }
17439
17440        var sAction = '/exec/front/order/basket/';
17441        action_basket(sType,'category',sAction,frm.serialize(),EC$.data(document,'sBasketType_class'));
17442        // 장바구니옵션창 자동으로 닫기게 처리-요거 처리 안하믄 레이어장바구니쪽에서 오류남 ECHOSTING-68196
17443        EC$('.xans-product-basketoption').remove();
17444    },
17445    getHiddenElement: function(sName, sValue)
17446    {
17447        return EC$('<input />').attr({
17448            'type': 'hidden',
17449            'name': sName,
17450            'value': sValue
17451        });
17452    },
17453    getBasketForm: function()
17454    {
17455        return EC$('<form>').attr({
17456            'method': 'POST',
17457            'name': 'CategoryBasket'
17458        });
17459    },
17460    /**
17461     * 리스트에서 상품 비교로 값을 넘긴다.
17462     */
17463    setProductCompare: function()
17464    {
17465        if (EC$('.ProductCompareClass:checked').length < 1) {
17466            alert(__('비교할 상품을 선택해 주세요.'));
17467            return false;
17468        } else {
17469            var aProductNo = new Array();
17470            EC$('.ProductCompareClass:checked').each(function()
17471            {
17472                var aClass = EC$(this).attr('class').split(' ');
17473
17474                var iSize = aClass.length;
17475                for (var x = 0; x < iSize; x++) {
17476                    var iProductNo = parseInt(aClass[x].split('_')[1],10);
17477                    if (aClass != '' && aClass[x].indexOf('ECPCNO_') == 1 && EC$.inArray(iProductNo,aProductNo) < 0) {
17478                        aProductNo.push(iProductNo);
17479                    }
17480                }
17481            });
17482            if (aProductNo.length > 1) {
17483                if (aProductNo.length > max_comp_number) {
17484                    alert(sprintf(__('%s개까지 비교 가능합니다.'), max_comp_number));
17485                } else {
17486                    var eForm = EC$('<form>').attr({
17487                        'method': 'get',
17488                        'action': sComparePageUrl
17489                    });
17490                    var iSize = aProductNo.length;
17491                    for (var x = 0; x < iSize; x++) {
17492                        EC$('<input />').attr({
17493                            'type': 'hidden',
17494                            'name': 'product_no[]'
17495                        }).val(aProductNo[x]).appendTo(eForm);
17496                    }
17497                    eForm.appendTo(EC$('body')).submit();
17498                }
17499            } else {
17500                alert(__('두개 이상의 상품을 선택하세요.'));
17501            }
17502        }
17503    },
17504    /**
17505     * 선택한상품만 주문하기
17506     *
17507     * @param string sOptionParam 옵션 파람값
17508     * @param int iProductNo 상품번호
17509     */
17510    selectbuy_action: function(iProductNo)
17511    {
17512        // ECHOSTING-95935 장바구니 상품 INSERT 실패 log방지
17513        if (typeof iProductNo === 'undefined') return;
17514
17515        var aOptionId = new Array();
17516        var aTargetElement = EC$('.' + EC$.data(document,'Check_class')+':checked');
17517        for (var x = 0; x < aTargetElement.length; x++) {
17518            var sOptionId = EC$(aTargetElement[x]).val();
17519            aOptionId.push("item_code[]=" + sOptionId);
17520        }
17521
17522        var sUrl = '/exec/front/order/basket/?command=select_prdcnt&product_no=' + iProductNo + '&option_type=T&' + aOptionId.join("&");
17523        EC$.ajax({
17524            url: sUrl,
17525            dataType: 'json',
17526            async: false,
17527            success: function(data) {
17528                if (data.result > 0 && !confirm(sprintf(__('동일상품이 장바구니에 %s개 있습니다.'), data.result) +'\n'+ __('함께 구매하시겠습니까?'))) {
17529                    sIsPrdOverride = 'T';
17530                }
17531            }
17532        });
17533    }
17534};
17535
17536var EC_ListAction = CAFE24.getDeprecatedNamespace('LIST_ACTION');
17537/**
17538 * 기존에 product_submit함수에 있던 내용들을 메소드 단위로 리펙토링한 객체
17539 */
17540var PRODUCTSUBMIT = {
17541    oConfig: {
17542        'sFormSelector': '#frm_image_zoom'
17543    },
17544    /**
17545     * 1 : 바로 구매, 2 : 장바구니 넣기
17546     */
17547    sType: null,
17548    sAction: null,
17549    oObject: null,
17550    oValidate: null,
17551    oForm: null,
17552    oDebug: null,
17553    bIsDebugConsoleOut: false,
17554    sPaymethod: null,
17555    aInfo: null,
17556
17557    /**
17558     * 초기화
17559     */
17560    initialize: function(sType, sAction, oObject, aInfo)
17561    {
17562        this.oDebug = this.DEBUG.initialize(this);
17563        this.oDebug.setInfo('PRODUCTSUBMIT.initialize 시작');
17564        this.oDebug.setInfo('sType : ', sType);
17565        this.oDebug.setInfo('sAction : ', sAction);
17566        this.oDebug.setInfo('oObject : ', oObject);
17567        this.oDebug.setInfo('aInfo : ', aInfo);
17568
17569        if (typeof(sType) === 'undefined' || ((sType !== 'sms_restock' && sType !== 'email_restock') && typeof(sAction) === 'undefined')) {
17570            this.oDebug.setMessage('PRODUCTSUBMIT.initialize fail');
17571            return false;
17572        }
17573        this.sType = sType;
17574        this.sAction = sAction;
17575        this.oObject = oObject;
17576        this.sPaymethod = $(oObject).data('paymethod');
17577        this.oValidate = this.VALIDATION.initialize(this);
17578        this.UTIL.initialize(this);
17579        this.oForm = EC$(this.oConfig.sFormSelector);
17580        this.oForm.find(':hidden').remove();
17581        this.aInfo = aInfo;
17582        NEWPRD_ADD_OPTION.initCustomData();
17583    },
17584    /**
17585     * 데이터 검증
17586     */
17587    isValidRequest: function()
17588    {
17589        try {
17590            this.oDebug.setInfo('PRODUCTSUBMIT.isValidRequest 시작');
17591
17592            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidFunding');
17593            if (this.oValidate.isValidFunding() === false) {
17594                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidFunding fail');
17595            }
17596
17597            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isRequireLogin');
17598            if (this.oValidate.isRequireLogin() === false) {
17599                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isRequireLogin fail');
17600            }
17601
17602            // ECHOSTING-370710: 적립금 전용 상품 비회원 구매 차단
17603            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isMileageOnlyForNonmember');
17604            if (this.oValidate.isMileageOnlyForNonmember() === false) {
17605                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isMileageOnlyForNonmember fail');
17606            }
17607
17608            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isPriceContent');
17609            if (this.oValidate.isPriceContent() === false) {
17610                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isPriceContent fail');
17611            }
17612
17613            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isOptionDisplay');
17614            if (this.oValidate.isOptionDisplay() === false) {
17615                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isOptionDisplay fail');
17616            }
17617
17618            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isItemInStock');
17619            if (this.oValidate.isItemInStock() === false) {
17620                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isItemInStock fail');
17621            }
17622
17623            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidRegularDelivery');
17624            if (this.oValidate.isValidRegularDelivery() === false) {
17625                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidRegularDelivery fail');
17626            }
17627
17628            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidOption');
17629            if (this.oValidate.isValidOption() === false) {
17630                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidOption fail');
17631            }
17632
17633            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidAddproduct');
17634            if (this.oValidate.isValidAddproduct() === false) {
17635                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidAddproduct fail');
17636            }
17637
17638            this.oDebug.setInfo('PRODUCTSUBMIT.setDynamicCheckoutAppData');
17639            if (this.setDynamicCheckoutAppData() === false) {
17640                this.oDebug.setMessage('PRODUCTSUBMIT.setDynamicCheckoutAppDatafail');
17641            }
17642
17643            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isExclusivePurchaseOnly');
17644            if (this.oValidate.isExclusivePurchaseOnly() === false) {
17645                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isExclusivePurchaseOnly fail');
17646            }
17647
17648            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidReservationItemData');
17649            if (this.oValidate.isValidReservationItemData() === false) {
17650                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidReservationItemData fail');
17651            }
17652
17653            this.oDebug.setInfo('PRODUCTSUBMIT.oValidate.isValidCulturalTax');
17654            if (this.oValidate.isValidCulturalTax() === false) {
17655                this.oDebug.setMessage('PRODUCTSUBMIT.oValidate.isValidCulturalTax fail');
17656            }
17657
17658        } catch (mError) {
17659            return this.DEBUG.messageOut(mError, 'PRODUCT_ACTION.isValidRequest');
17660        }
17661        return true;
17662    },
17663    /**
17664     * 전송폼 생성
17665     */
17666    setBasketForm: function()
17667    {
17668        try {
17669            this.oDebug.setInfo('PRODUCTSUBMIT.setBasketForm 시작');
17670            // 예약 주문 체크
17671            STOCKTAKINGCHECKRESERVE.checkReserve();
17672
17673            this.oForm.attr('method', 'POST');
17674            this.oForm.attr('action', '/' + this.sAction);
17675
17676            this.oDebug.setInfo('PRODUCTSUBMIT.setCommonInput');
17677            if (this.setCommonInput() === false) {
17678                this.oDebug.setMessage('PRODUCTSUBMIT.setCommonInput fail');
17679            }
17680
17681            this.oDebug.setInfo('PRODUCTSUBMIT.setOptionId');
17682            if (this.setOptionId() === false) {
17683                this.oDebug.setMessage('PRODUCTSUBMIT.setOptionId fail');
17684            }
17685
17686            this.oDebug.setInfo('PRODUCTSUBMIT.setAddOption');
17687            if (this.setAddOption() === false) {
17688                this.oDebug.setMessage('PRODUCTSUBMIT.setAddOption fail');
17689            }
17690
17691            this.oDebug.setInfo('PRODUCTSUBMIT.setQuantityOveride');
17692            if (this.setQuantityOveride() === false) {
17693                this.oDebug.setMessage('PRODUCTSUBMIT.setQuantityOveride fail');
17694            }
17695
17696            this.oDebug.setInfo('PRODUCTSUBMIT.setSelectedItem');
17697            if (this.setSelectedItemHasOptionT() === false || this.setSelectedItemHasOptionF() === false) {
17698//                if (this.setSelectedItemHasOptionT() === false || this.setSelectedItemHasOptionF() === false || this.setSingleSelectedItem() === false) {
17699                this.oDebug.setMessage('PRODUCTSUBMIT.setSelectedItem fail');
17700            }
17701
17702            this.oDebug.setInfo('PRODUCTSUBMIT.setFundingData');
17703            if (this.setFundingData() === false) {
17704                this.oDebug.setMessage('PRODUCTSUBMIT.setFundingData fail');
17705            }
17706            this.oDebug.setInfo('PRODUCTSUBMIT.setRegularDelivery');
17707            if (this.setRegularDelivery() === false) {
17708                this.oDebug.setMessage('PRODUCTSUBMIT.setRegularDelivery fail');
17709            }
17710
17711            this.oDebug.setInfo('PRODUCTSUBMIT.setReservationData');
17712            if (this.setReservationData() === false) {
17713                this.oDebug.setMessage('PRODUCTSUBMIT.setReservationData fail');
17714            }
17715
17716            this.oDebug.setInfo('PRODUCTSUBMIT.setCulturalTax');
17717            if (this.setCulturalTax() === false) {
17718                this.oDebug.setMessage('PRODUCTSUBMIT.setCulturalTax fail');
17719            }
17720
17721        } catch (mError) {
17722            return this.DEBUG.messageOut(mError, 'PRODUCT_ACTION.setBasketForm');
17723        }
17724
17725        return true;
17726    },
17727    setBasketAjax : function(defer)
17728    {
17729        this.oDebug.setInfo('PRODUCTSUBMIT.setBasketAjax 시작');
17730        if (typeof(ACEWrap) !== 'undefined') {
17731            // 에이스카운터
17732            ACEWrap.addBasket();
17733        }
17734
17735        if (typeof(PRODUCTSUBMIT.sPaymethod) !== 'undefined') {
17736            this.oForm.prepend(getInputHidden('paymethod', this.sPaymethod));
17737        }
17738
17739        // 파일첨부 옵션의 파일업로드가 없을 경우 바로 장바구니에 넣기
17740        if (FileOptionManager.existsFileUpload() === false) {
17741            NEWPRD_ADD_OPTION.setItemPerAddOptionForm(this.oForm);
17742            action_basket(this.sType, 'detail', this.sAction, this.oForm.serialize(), this.UTIL.getData('sBasketType'), null, defer);
17743        } else {
17744            // 파일첨부 옵션의 파일업로드가 있으면
17745            FileOptionManager.upload(function(mResult) {
17746                // 파일업로드 실패
17747                if (mResult === false) {
17748                    PRODUCTSUBMIT.DEBUG.setMessage('PRODUCTSUBMIT.setBasketAjax fail - 파일업로드 실패');
17749                    return false;
17750                }
17751
17752                // 파일업로드 성공
17753                for (var sId in mResult) {
17754                    // 해당 품목에 파일 첨부 옵션 항목 추가
17755                    NEWPRD_ADD_OPTION.pushFileList(sId, mResult);
17756                    PRODUCTSUBMIT.UTIL.appendHidden(sId, FileOptionManager.encode(mResult[sId]));
17757                }
17758
17759                NEWPRD_ADD_OPTION.setItemPerAddOptionForm(PRODUCTSUBMIT.oForm);
17760                action_basket(PRODUCTSUBMIT.sType, 'detail', PRODUCTSUBMIT.sAction, PRODUCTSUBMIT.oForm.serialize(), PRODUCTSUBMIT.UTIL.getData('sBasketType'), null, defer);
17761            });
17762        }
17763    },
17764    setSelectedItem: function(sItemCode, iQuantity, sParameterName, sAdditionalData)
17765    {
17766        iQuantity = parseInt(iQuantity, 10);
17767        if (isNaN(iQuantity) === true || iQuantity < 1) {
17768            this.oDebug.setMessage('PRODUCTSUBMIT.setSelectedItem fail - iQuantity Fault');
17769            return false;
17770        }
17771
17772        if (typeof(sItemCode) !== 'string') {
17773            this.oDebug.setMessage('PRODUCTSUBMIT.setSelectedItem fail - sItemCode Fault');
17774            return false;
17775        }
17776
17777        if (typeof(sParameterName) === 'undefined') {
17778            sParameterName = 'selected_item[]';
17779        }
17780
17781        if (typeof(sAdditionalData) === 'undefined') {
17782            sAdditionalData = '';
17783        } else {
17784            sAdditionalData = '||' + sAdditionalData;
17785        }
17786
17787        this.UTIL.prependHidden(sParameterName, iQuantity+'||'+sItemCode+sAdditionalData);
17788        return true;
17789    },
17790    getQuantity: function(oQuantityElement)
17791    {
17792        if (typeof(quantity_id) === 'undefined') {
17793            var quantity_id = '#quantity';
17794        }
17795        var $oQuantityElement = EC$(quantity_id);
17796        if (typeof(oQuantityElement) === 'object') {
17797            $oQuantityElement = oQuantityElement;
17798        }
17799        return parseInt($oQuantityElement.val(),10);
17800    },
17801    setSelectedItemHasOptionF: function()
17802    {
17803        if (has_option !== 'F') {
17804            return true;
17805        }
17806
17807        if (item_code === undefined) {
17808            var sItemCode = product_code+'000A';
17809        } else {
17810            var sItemCode = item_code;
17811        }
17812        if (this.sType === 'funding') {
17813            CAFE24.SHOP_FRONT_PRODUCT_FUNDING.setStandaloneProductItem(sItemCode);
17814        } else {
17815            if (NEWPRD_ADD_OPTION.checkSoldOutProductValid(this.oObject) === false && CAFE24.SHOP_FRONT_PRODUCT_RESTOCK.isRestock(this.sType) === false) {
17816                this.setSelectedItem(sItemCode, this.getQuantity());
17817            }
17818        }
17819
17820        return true;
17821    },
17822    setEtypeSelectedItem: function(bFormAppend)
17823    {
17824        var _sItemCode = sProductCode + '000A';
17825        if (this.sType === 'reservation') {
17826            _sItemCode = '';
17827
17828        }
17829        var iQuantity = 0;
17830        var sSelectedItemByEtype = '';
17831        var _aItemValueNo = '';
17832        if (isNewProductSkin() === false) {
17833            iQuantity = this.getQuantity();
17834
17835            // 수량이 없는 경우에는 최소 구매 수량으로 던진다!!
17836            if (iQuantity === undefined) {
17837                iQuantity = product_min;
17838            }
17839            var _aItemValueNo = Olnk.getSelectedItemForBasketOldSkin(sProductCode, EC$('[id^="product_option_id"]'), iQuantity);
17840
17841            if (_aItemValueNo.bCheckNum === false) {
17842                _aItemValueNo = Olnk.getProductAllSelected(sProductCode , EC$('[id^="product_option_id"]') , iQuantity);
17843                if (_aItemValueNo === false) {
17844                    this.oDebug.setMessage('etype error');
17845                }
17846            }
17847            sSelectedItemByEtype = 'selected_item_by_etype[]='+EC$.toJSON(_aItemValueNo) + '&';
17848            if (bFormAppend === true) {
17849                this.setSelectedItem(_sItemCode, iQuantity);
17850                this.UTIL.appendHidden('selected_item_by_etype[]', EC$.toJSON(_aItemValueNo));
17851            }
17852        } else {
17853            var bIsProductEmptyOption = this.UTIL.getData('bIsProductEmptyOption');
17854            // 메인상품 선택여부 확인 false : 선택함 || true : 선택안함
17855            if (bIsProductEmptyOption === false && NEWPRD_ADD_OPTION.checkSoldOutProductValid(this.oObject) === false) {
17856                var iOptionBoxLength = EC$('.option_box_id').length - 1;
17857                EC$('.option_box_id').each(function (i) {
17858                    var sQuantityElement = EC$('#' + EC$(this).attr('id').replace('id', 'quantity'));
17859                    if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === true) {
17860                        sQuantityElement = EC$('#quantity_'+EC$(this).attr('composition-code'));
17861                    }
17862                    iQuantity = PRODUCTSUBMIT.getQuantity(sQuantityElement);
17863                    _aItemValueNo = Olnk.getSelectedItemForBasket(sProductCode, EC$(this), iQuantity);
17864
17865                    if (_aItemValueNo.bCheckNum === false) { // 옵션박스는 있지만 값이 선택이 안된경우
17866                        _aItemValueNo = Olnk.getProductAllSelected(sProductCode, EC$(this), iQuantity);
17867                    }
17868                    if (bFormAppend === true) {
17869                        PRODUCTSUBMIT.setSelectedItem(_sItemCode, iQuantity);
17870                        PRODUCTSUBMIT.UTIL.prependHidden('selected_item_by_etype[]', EC$.toJSON(_aItemValueNo));
17871                    }
17872                    sSelectedItemByEtype += 'selected_item_by_etype[]='+EC$.toJSON(_aItemValueNo) + '&';
17873                    var oItem = EC$('[name="item_code[]"]').eq(i);
17874                    var sItemCode = _sItemCode + '_' + i;
17875
17876                    //품목별 추가옵션 셋팅
17877                    if (bFormAppend === true) {
17878                        var ePerAddOption = EC$('.option_products .option').eq(i).find(".input_addoption:visible");
17879                        if (ePerAddOption.length > 0) { // 옵션 박스안에서 개별 입력시
17880                            sItemCode = Olnk.getCustomOptionItemCode(sProductCode, iOptionBoxLength, i);
17881                            NEWPRD_ADD_OPTION.setItemPerAddOptionData(sItemCode, ePerAddOption, PRODUCTSUBMIT.oForm);
17882                        } else {
17883                            //품목별 추가옵션 셋팅
17884                            var sItemAddOption = NEWPRD_ADD_OPTION.getAddOptionValue(oItem.attr('data-item-add-option'));
17885                            NEWPRD_ADD_OPTION.setItemAddOption(sItemCode, sItemAddOption, PRODUCTSUBMIT.oForm);
17886                        }
17887                    }
17888                });
17889
17890                // 전부 선택인 경우 필요값 생성한다.
17891                if (_aItemValueNo === '') {
17892                    iQuantity = this.getQuantity();
17893                    _aItemValueNo = Olnk.getProductAllSelected(sProductCode, EC$('[id^="product_option_id"]'), iQuantity);
17894                    if (_aItemValueNo !== false) {
17895                        if (bFormAppend === true) {
17896                            this.setSelectedItem(_sItemCode, iQuantity);
17897                            this.UTIL.prependHidden('selected_item_by_etype[]', EC$.toJSON(_aItemValueNo));
17898                        }
17899                        sSelectedItemByEtype += 'selected_item_by_etype[]='+EC$.toJSON(_aItemValueNo) + '&';
17900                    }
17901                }
17902            }
17903        }
17904        this.UTIL.setData('sSelectedItemByEtype', sSelectedItemByEtype);
17905    },
17906    setSelectedItemHasOptionT: function()
17907    {
17908        if (has_option !== 'T') {
17909            return true;
17910        }
17911
17912        if (Olnk.isLinkageType(sOptionType) === true) {
17913            this.setEtypeSelectedItem(true);
17914        } else {
17915            if (isNewProductSkin() === true && NEWPRD_ADD_OPTION.checkSoldOutProductValid(this.oObject) === false) {
17916                if (this.sType === 'funding') {
17917                    EC$('.xans-product-funding').each(function(i) {
17918                        if (EC$(this).find('.EC-funding-checkbox:checked').length !== 1) {
17919                            return;
17920                        }
17921                        var iQuantity = EC$(this).find('input.quantity').val();
17922                        var sItemCode = EC$(this).find('input.selected-funding-item').val();
17923                        PRODUCTSUBMIT.setSelectedItem(sItemCode, iQuantity);
17924                    });
17925
17926                } else {
17927                    if (EC$('[name="quantity_opt[]"][id^="option_box"]').length > 0 && EC$('[name="quantity_opt[]"][id^="option_box"]').length == EC$('[name="item_code[]"]').length) {
17928
17929                        EC$('[name="quantity_opt[]"][id^="option_box"]').each(function(i) {
17930
17931                            var oItem = EC$('[name="item_code[]"]').eq(i);
17932                            var sItemCode = oItem.val();
17933                            PRODUCTSUBMIT.setSelectedItem(sItemCode, PRODUCTSUBMIT.getQuantity(EC$(this)));
17934
17935                            //품목별 추가옵션 셋팅
17936                            var eProductBox = EC$('.option_products .option');
17937                            if (CAFE24.checkChannelUI() === true) {
17938                                eProductBox = EC$('.option_product[data-option-index]');
17939                            }
17940                            var ePerAddOption = eProductBox.eq(i).find(".input_addoption:visible");
17941                            if (ePerAddOption.length > 0) { // 옵션 박스안에서 개별 입력시
17942                                NEWPRD_ADD_OPTION.setItemPerAddOptionData(sItemCode, ePerAddOption, PRODUCTSUBMIT.oForm);
17943                            } else {
17944                                var sItemAddOption = NEWPRD_ADD_OPTION.getAddOptionValue(oItem.attr('data-item-add-option'));
17945                                NEWPRD_ADD_OPTION.setItemAddOption(sItemCode, sItemAddOption, PRODUCTSUBMIT.oForm);
17946                            }
17947                        });
17948                    }
17949                }
17950
17951            } else {
17952                // 뉴 상품 + 구스디 스킨
17953                var aItemCode = ITEM.getItemCode();
17954                for (var i = 0; i < aItemCode.length; i++) {
17955                    var sItemCode = aItemCode[i];
17956                    this.setSelectedItem(sItemCode, this.getQuantity(i));
17957                }
17958            }
17959        }
17960
17961        if (this.sType === 'sms_restock' || this.sType === 'email_restock') {
17962            this.UTIL.appendHidden('item_code', EC$(this.oObject).attr('item_code'));
17963        }
17964
17965        // 예약상품인 경우 selected_item 처리
17966
17967        return true;
17968    },
17969    setQuantityOveride: function()
17970    {
17971        if (this.sType !== 1 && this.sType !== 'naver_checkout' && this.sType !== 'direct_buy' && this.sType !== 'simple_pay' && this.sType !== 'app' && this.sType !== 'checkout') {
17972            return true;
17973        }
17974
17975        // 전역변수임
17976        sIsPrdOverride = 'F';
17977        if (this.sType === 1 || this.sType == 'simple_pay') {
17978            var aItemParams = [];
17979            var aItemCode = ITEM.getItemCode();
17980            for (var i = 0, length = aItemCode.length; i < length; i++) {
17981                aItemParams.push("item_code[]=" + aItemCode[i]);
17982            }
17983            var sOptionParam = this.UTIL.getData('sOptionParam');
17984            sOptionParam = sOptionParam + '&delvtype=' + delvtype + '&' + aItemParams.join("&");
17985            if (Olnk.isLinkageType(sOptionType) === true) {
17986                this.setEtypeSelectedItem();
17987                var sSelectedItemByEtype = this.UTIL.getData('sSelectedItemByEtype', sSelectedItemByEtype);
17988            }
17989            selectbuy_action(sOptionParam, iProductNo, sSelectedItemByEtype);
17990        }
17991
17992        if (this.sType === 'naver_checkout' || this.sType === 'direct_buy' || this.sType === 'app' || this.sType === 'checkout') {
17993            sIsPrdOverride = 'T';
17994        }
17995        this.UTIL.appendHidden('quantity_override_flag', sIsPrdOverride);
17996    },
17997    /**
17998     * 실제 옵션에 대한 검증이 아니라 구상품과의 호환을 위해 존재하는 파라미터들을 세팅해주는 메소드
17999     */
18000    setOptionId: function()
18001    {
18002        var count = 1;
18003        var sOptionParam = '';
18004        EC$('select[id^="' + product_option_id + '"]').each(function()
18005        {
18006            PRODUCTSUBMIT.UTIL.appendHidden('optionids[]', EC$(this).attr('name'));
18007            if (EC$(this).prop('required') === true || EC$(this).attr('required') === 'required') {
18008                PRODUCTSUBMIT.UTIL.appendHidden('needed[]', EC$(this).attr('name'));
18009            }
18010            var iSelectedIndex = EC$(this).get(0).selectedIndex;
18011            if (EC$(this).prop('required') && iSelectedIndex > 0) iSelectedIndex -= 1;
18012
18013            if (iSelectedIndex > 0) {
18014                sOptionParam += '&option' + count + '=' + iSelectedIndex;
18015                var sValue = EC$(this).val();
18016                var aValue = sValue.split("|");
18017                PRODUCTSUBMIT.UTIL.appendHidden(EC$(this).attr('name'), aValue[0]);
18018                ++count;
18019            }
18020        });
18021        this.UTIL.setData('sOptionParam', sOptionParam);
18022    },
18023    setAddOption: function()
18024    {
18025        if (add_option_name.length === 0) {
18026            return;
18027        }
18028        if (this.sType === 'funding') {
18029            // CAFE24.SHOP_FRONT_PRODUCT_FUNDING.getFundingBasketData를 참조하세요.
18030            return;
18031        }
18032
18033        var iAddOptionNo = 0;
18034        var aAddOptionName = [];
18035        if (has_option === 'F') {
18036            NEWPRD_ADD_OPTION.addItem(item_code);
18037            var iAddOptionIndex = NEWPRD_ADD_OPTION.getLastIndex();
18038        }
18039        for (var i = 0, iAddOptionNameLength = add_option_name.length; i < iAddOptionNameLength; i++) {
18040            var sValue = EC$('#' + add_option_id + i).val();
18041            if (typeof sValue === 'undefined') {
18042                continue;
18043            }
18044            if (has_option === 'F') {
18045                NEWPRD_ADD_OPTION.addCustomOption(iAddOptionIndex, {
18046                    type: 'text',
18047                    value: sValue,
18048                    info: add_option_name[i]
18049                }, 'input');
18050            }
18051            if (sValue === '') {
18052                continue;
18053            }
18054            this.UTIL.appendHidden('option_add[]', sValue);
18055            aAddOptionName[iAddOptionNo++] = add_option_name[i];
18056        }
18057        this.UTIL.appendHidden('add_option_name', aAddOptionName.join(';'));
18058        NEWPRD_ADD_OPTION.setItemAddOptionName(this.oForm); // 품목별 추가옵션명인데 왜 상품단위로 도는지 확인이 필요함
18059    },
18060    setFundingData: function()
18061    {
18062        if (this.sType !== 'funding') {
18063            return true;
18064        }
18065        if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING.getFundingBasketData !== 'function') {
18066            this.oDebug.setMessage('CAFE24.SHOP_FRONT_PRODUCT_FUNDING.getFundingBasketData error');
18067            return false;
18068        }
18069
18070        var oFundingBasketData = CAFE24.SHOP_FRONT_PRODUCT_FUNDING.getFundingBasketData();
18071        if (typeof(oFundingBasketData) !== 'object') {
18072            this.oDebug.setMessage(oFundingBasketData.sMessage);
18073            return false;
18074        }
18075
18076        delete oFundingBasketData.sMessage;
18077        delete oFundingBasketData.bIsResult;
18078        this.UTIL.appendHidden(oFundingBasketData);
18079
18080
18081    },
18082    setRegularDelivery : function()
18083    {
18084        if (PRODUCTSUBMIT.isRegularDelivery() === false) {
18085            return true;
18086        }
18087
18088        var sSubscriptionCycleValue = PRODUCTSUBMIT.getRegularSelectedValue();
18089
18090        // 디자인상에서 별도 처리 된 정기배송
18091        if (typeof(sSubscriptionCycleValue) === 'undefined') {
18092            return true;
18093        }
18094
18095        // 기존 하드코딩용
18096        var regex = /[W|M|Y]$/g;
18097        if (regex.test(sSubscriptionCycleValue) === false) {
18098            sSubscriptionCycleValue = sSubscriptionCycleValue + 'W';
18099        }
18100
18101        var sSubscriptionCycleCount = sSubscriptionCycleValue.substring(sSubscriptionCycleValue.length-1, -1);
18102        var sSubscriptionCycle = sSubscriptionCycleValue.slice(-1);
18103
18104        this.UTIL.appendHidden('is_subscription', EC$('.EC_regular_delivery:checked').val());
18105        this.UTIL.appendHidden('subscription_cycle', sSubscriptionCycle); // 주단위 현재는 고정
18106        this.UTIL.appendHidden('subscription_cycle_count', sSubscriptionCycleCount);
18107
18108        var sSubscriptionCountValue = PRODUCTSUBMIT.getRegularCountSelectedValue();
18109
18110        // 디자인상에서 별도 처리 된 정기배송
18111        if (typeof(sSubscriptionCountValue) === 'undefined') {
18112            return true;
18113        }
18114        this.UTIL.appendHidden('max_expected_pay_seq', sSubscriptionCountValue);
18115    },
18116    setReservationData : function()
18117    {
18118        // 예약상품이 아니므로 다른 작업은 하지 않고 패스
18119        if (this.sType !== 'reservation') {
18120            return true;
18121        }
18122        this.UTIL.appendHidden('product_service_type', 'reservation');
18123        this.UTIL.appendHidden('reservation_type', (EC$('input#reservation_type').val() ?? ''));
18124        this.UTIL.appendHidden('service_start_date', (EC$('input#service_start_date').val() ?? ''));
18125        this.UTIL.appendHidden('service_end_date', (EC$('input#service_end_date').val() ?? ''));
18126        this.UTIL.appendHidden('quantity_override_flag', 'T');
18127    },
18128    setCulturalTax : function()
18129    {
18130        this.UTIL.appendHidden('is_cultural_tax', SHOP_FRONT_PRODUCT_CULTURAL_TAX.getIsCulturalTax());
18131        return true;
18132    },
18133    setDynamicCheckoutAppData : function()
18134    {
18135        if (this.sType !== 'app') {
18136            return true;
18137        }
18138
18139        this.UTIL.appendHidden(this.aInfo);
18140    },
18141
18142    setCommonInput : function()
18143    {
18144        var sBasketType = (typeof(basket_type) === 'undefined') ? 'A0000' : basket_type;
18145        this.UTIL.setData('sBasketType', sBasketType);
18146
18147        var oCommon = {
18148            'product_no': iProductNo,
18149            'product_name': product_name,
18150            'main_cate_no': iCategoryNo,
18151            'display_group': iDisplayGroup,
18152            'option_type': option_type,
18153            'product_min': product_min,
18154            'command': 'add',
18155            'has_option': has_option,
18156            'product_price': product_price,
18157            'multi_option_schema': EC$('#multi_option').html(),
18158            'multi_option_data': '',
18159            'delvType': delvtype,
18160            'redirect': this.sType,
18161            'product_max_type': product_max_type,
18162            'product_max': product_max,
18163            'basket_type': sBasketType,
18164            'ch_ref': CAFE24.getChRefData()
18165        };
18166        // 상품명에 특수문자가 들어가면 인코딩/디코딩 관계로 레이어가 부모창을 검증하지 못하는 이슈가 발생하고, 쓰이지 않는 변수로 판단되어 제거
18167        if (this.sType === 'sms_restock') {
18168            delete oCommon.product_name;
18169        }
18170        this.UTIL.appendHidden(oCommon);
18171
18172        if (typeof(CAPP_FRONT_OPTION_SELECT_BASKETACTION) !== 'undefined' && CAPP_FRONT_OPTION_SELECT_BASKETACTION === true) {
18173            this.UTIL.appendHidden('basket_page_flag', 'T');
18174        } else {
18175            var oDlvChannelElement = EC$('#delivery_cost_prepaid_channel > li.selected');
18176            if (oDlvChannelElement.length > 0) {
18177                this.UTIL.appendHidden('prd_detail_ship_type', oDlvChannelElement.attr('data-value'));
18178            } else {
18179                this.UTIL.appendHidden('prd_detail_ship_type', EC$('#delivery_cost_prepaid').val());
18180            }
18181        }
18182        if (this.sType !== 'funding') {
18183            // 수량 체크
18184            var iQuantity = 1;
18185            if (CAFE24.SHOP_FRONT_PRODUCT_RESTOCK.isRestock(this.sType) === false) {
18186                iQuantity = checkQuantity();
18187                if (iQuantity == false) {
18188                    // 현재 관련상품 선택 했는지 여부 확인
18189                    // 관련 상품 자체가 없을때는 뒤에 저 로직을 탈 필요가 없음(basket_info 관련상품 체크박스)
18190                    if (EC$('input[name="basket_info[]"]').length <= 0 || NEWPRD_ADD_OPTION.checkRelationProduct(this.oObject, this.sType) === false) {
18191                        return false;
18192                    }
18193                }
18194            }
18195
18196            // 폼 세팅
18197            if (iQuantity == undefined || isNaN(iQuantity) === true || iQuantity < 1) {
18198                iQuantity = 1;
18199            }
18200            this.UTIL.appendHidden('quantity', iQuantity);
18201        }
18202
18203        // 바로구매 주문서 여부
18204        if (this.sType == 'direct_buy') {
18205            this.UTIL.appendHidden('is_direct_buy', 'T');
18206        } else {
18207            this.UTIL.appendHidden('is_direct_buy', 'F');
18208        }
18209    },
18210    VALIDATION: {
18211        initialize: function(oParent)
18212        {
18213            this.parent = oParent;
18214            return this;
18215        },
18216        isRequireLogin: function()
18217        {
18218            // ECHOSTING-58174
18219            if (sIsDisplayNonmemberPrice !== 'T') {
18220                return true;
18221            }
18222            switch (this.parent.sType) {
18223                case 1 :
18224                case 'simple_pay' : // 간편결제
18225                    alert(__('로그인후 상품을 구매해주세요.'));
18226                    break;
18227                case 2 :
18228                    alert(__('로그인후 장바구니 담기를 해주세요.'));
18229                     break;
18230                case 'direct_buy' :
18231                    alert(__('회원만 구매 가능합니다. 비회원인 경우 회원가입 후 이용하여 주세요.'));
18232                    break;
18233                default :
18234                    break;
18235            }
18236            btn_action_move_url('/member/login.html');
18237            return false;
18238        },
18239        // ECHOSTING-370710: 적립금 전용 상품은 비회원 구매 불가 - 상품상세에서 로그인 유도
18240        isMileageOnlyForNonmember: function()
18241        {
18242            if (typeof EC_FRONT_JS_CONFIG_SHOP === 'undefined' || EC_FRONT_JS_CONFIG_SHOP.sIsMileageOnlyProduct !== 'T') {
18243                return true;
18244            }
18245            if (EC_FRONT_JS_CONFIG_SHOP.sIsLoginMember === 'T') {
18246                return true;
18247            }
18248            alert(__('회원만 주문 가능한 상품이 있습니다. 로그인 후 이용해 주세요.'));
18249            btn_action_move_url('/member/login.html');
18250            return false;
18251        },
18252        isPriceContent: function()
18253        {
18254            if (typeof(product_price_content) === 'undefined') {
18255                return true;
18256            }
18257
18258            var sProductcontent = product_price_content.replace(/\s/g, '').toString();
18259            if (sProductcontent === '1') {
18260                alert(sprintf(__('%s 상품은 구매할 수 있는 상품이 아닙니다.'), product_name));
18261                return false;
18262            }
18263
18264            return true;
18265        },
18266        isOptionDisplay: function()
18267        {
18268            if (typeof CAFE24.SHOP_FRONT_NEW_OPTION_COMMON !== 'undefined'
18269                && has_option === 'T'
18270                && Olnk.isLinkageType(sOptionType) === false
18271                && CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isValidOptionDisplay(product_option_id) === false) {
18272
18273                alert(sprintf(__('%s 상품은 구매할 수 있는 상품이 아닙니다.'), product_name));
18274                return false;
18275            }
18276            return true;
18277        },
18278        isItemInStock: function()
18279        {
18280            if (CAFE24.SHOP_FRONT_PRODUCT_RESTOCK.isRestock(this.parent.sType) === false && (EC$('.option_box_id').length == 0 && EC$('.soldout_option_box_id').length > 0) === true) {
18281                alert(__('품절된 상품은 구매가 불가능합니다.'));
18282                return false;
18283            }
18284
18285            return true;
18286        },
18287        isValidOption: function()
18288        {
18289            // 필수옵션 체크
18290            var bIsProductEmptyOption = CAFE24.SHOP_FRONT_PRODUCT_RESTOCK.isRestock(this.parent.sType) === false && checkOptionRequired() == false;
18291            this.parent.UTIL.setData('bIsProductEmptyOption', bIsProductEmptyOption);
18292
18293            //추가구성상품 옵션 체크
18294            var oValidAddProductCount = NEWPRD_ADD_OPTION.isValidAddOptionSelect(this.parent.oForm);
18295
18296            //관련상품 옵션 체크
18297            var oValidRelationProductCount = NEWPRD_ADD_OPTION.isValidRelationProductSelect(this.parent.oForm, this.parent.oObject, bIsProductEmptyOption);
18298
18299            // 개별 구매 관련 검증된 데이터
18300            var oIndividualValidData = NEWPRD_ADD_OPTION.getIndividualValidCheckData(oValidRelationProductCount, oValidAddProductCount, bIsProductEmptyOption, this.parent.oForm);
18301
18302            // 옵션 체크
18303            if (bIsProductEmptyOption === true) {
18304                // 실패 타입 존재 할 경우
18305                if (oIndividualValidData.sFailType !== '') {
18306                    return false;
18307                }
18308                //관련상품 및 추가구성상품 단독구매시 유효성 메시지 노출여부 결정(순차 검증진행 추가 or 관련 + 본상품)
18309                if (NEWPRD_ADD_OPTION.checkIndividualValidAction(oValidRelationProductCount, oValidAddProductCount) === false) {
18310                    return false;
18311                }
18312                // 독립형 일때
18313                var oExistRequiredSelect = (option_type === 'F') ? EC$('select[id^="' + product_option_id + '"][required="true"]') : false;
18314                var sMsg = __('필수 옵션을 선택해주세요.');
18315                try {
18316                    // 관련상품 체크 확인 유무
18317                    if (NEWPRD_ADD_OPTION.checkRelationProduct(this.parent.oObject, this.parent.sType) === false) {
18318                        return false;
18319                    }
18320
18321                    if (oIndividualValidData.isValidInidual === false && CAFE24.SHOP_FRONT_PRODUCT_OPTIONLAYER.setLayer(iProductNo, iCategoryNo, 'normal') === true) {
18322                        return false;
18323                    }
18324
18325                    if (Olnk.getOptionPushbutton(EC$('#option_push_button')) === true) {
18326                        var bCheckOption = false;
18327                        EC$('select[id^="' + product_option_id + '"]').each(function() {
18328                            if (EC$(this).prop('required') === true && Olnk.getCheckValue(EC$(this).val(),'') === false) {
18329                                bCheckOption = true;
18330                                return false;
18331                            }
18332                        });
18333                        if (bCheckOption === false) {
18334                            sMsg = __('품목을 선택해 주세요.');
18335                        }
18336                    }
18337                } catch (e) {
18338                    PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.isValidOption');
18339                }
18340
18341                // 메인상품 품목데이터 확인
18342                var isEmptyItemData = ITEM.getItemCode().length == false || ITEM.getItemCode() === false;
18343                // 추가구성상품 및 관련상품의 개별적 구매
18344                if (isEmptyItemData === true && oIndividualValidData.isValidInidual === true) {
18345                    if (NEWPRD_ADD_OPTION.checkVaildIndividualMsg(
18345oIndividualValidData, this.parent.sType, this.parent.oObject) === false) {
18346                        return false;
18347                    }
18348
18349                } else {
18350                    if (option_type === 'F') {
18351                        var aRequiredFalseOption = [];
18352                        var aItemCodeList = ITEM.getItemCode();
18353                        // 필수 옵션정보와 선택한 옵션 정보 비교
18354                        for (var i = 0; i < aItemCodeList.length; i++) {
18355                            var sTargetItemCode = aItemCodeList[i];
18356                            // 선택되지않은 필수옵션정보
18357                            EC$('select[id^="' + product_option_id + '"][required="true"]').each(function () {
18358                                var bResult = false;
18359                                var sProductOptionId =  EC$(this).attr('id');
18360
18361                                EC$(this).find('option').each(function () {
18362                                    if (EC$(this).val() == sTargetItemCode) {
18363                                        bResult = true;
18364                                    }
18365                                });
18366                                if (bResult === false) {
18367                                    aRequiredFalseOption.push(sProductOptionId);
18368                                }
18369                            });
18370                        }
18371                        // 실제 첫번째 필수누락 옵션 selector
18372                        oExistRequiredSelect = EC$('select[id="' + aRequiredFalseOption[0] + '"][required="true"]');
18373
18374                        // 품절품목과 비교
18375                        EC$('.soldout_option_box_id').each(function () {
18376                            var sTargetItemCode = EC$(this).val();
18377                            for (var i = 0; i < aRequiredFalseOption.length; i++) {
18378                                EC$('select[id="' + aRequiredFalseOption[i] + '"][required="true"] option').each(function () {
18379                                    if (EC$(this).val() == sTargetItemCode) {
18380                                        // 품절 옵션 selector
18381                                        oExistRequiredSelect = EC$(this).parent();
18382                                        sMsg = __('품절된 상품은 구매가 불가능합니다.');
18383                                    }
18384                                });
18385                            }
18386                        });
18387                    }
18388                    // 기존 유효성 검증 메세지
18389                    var sOrginalValidMsg = NEWPRD_ADD_OPTION.checkExistingValidMessage(this.parent.oObject, oValidAddProductCount);
18390                    //추가구성상품의 선택되어있으면서 본상품의 옵션이 선택 안되었을때
18391                    sMsg = (sOrginalValidMsg === false) ? sMsg : sOrginalValidMsg;
18392
18393                    alert(sMsg);
18394                    if (oExistRequiredSelect !== false) {
18395                        oExistRequiredSelect.focus();
18396                    }
18397                    return false;
18398                }
18399            } else {
18400                // 관련상품 체크 확인
18401                if (NEWPRD_ADD_OPTION.checkRelationProduct(this.parent.oObject, this.parent.sType) === false) {
18402                    return false;
18403                }
18404
18405                // 단독구매시 메인상품 품절된 상품일때 메시지 처리
18406                if (NEWPRD_ADD_OPTION.checkSoldOutProductValid(this.parent.oObject) === true) {
18407                    this.parent.UTIL.appendHidden('is_product_sold_out', 'T');
18408                    if (NEWPRD_ADD_OPTION.checkVaildIndividualMsg(
18408oIndividualValidData, this.parent.sType, this.parent.oObject) === false) {
18409                        return false;
18410                    }
18411                }
18412                if (FileOptionManager.checkValidation() === false) {
18413                    return false;
18414                }
18415            }
18416            if (oValidAddProductCount.result === false) {
18417                if (oValidAddProductCount.message !== '') {
18418                    alert(oValidAddProductCount.message);
18419                    oValidAddProductCount.object.focus();
18420                }
18421                return false;
18422            }
18423            if (oValidRelationProductCount.result === false) {
18424                if (oValidRelationProductCount.message !== '') {
18425                    alert(oValidRelationProductCount.message);
18426                    oValidRelationProductCount.object.focus();
18427                }
18428                return false;
18429            }
18430            if (oIndividualValidData.isValidInidual === false) {
18431                // 추가 옵션 체크 (품목기반 추가옵션일때는 폼제출때 검증 불필요)
18432                var oParent = (this.parent.sType === 'funding') ? EC$('.EC-funding-checkbox:checked').parents('.xans-product-funding') : null;
18433                if (NEWPRD_ADD_OPTION.isItemBasedAddOptionType() !== true && checkAddOption(null, oParent) === false) {
18434                    this.parent.oDebug.setMessage('checkAddOption Fail');
18435                    return false;
18436                }
18437            }
18438
18439            if (NEWPRD_ADD_OPTION.checkPerAddInputOption() === false) {
18440                // 품목별 추가 옵션 입력시 체크
18441                return false;
18442            }
18443            return true;
18444        },
18445        isValidAddproduct: function()
18446        {
18447            if (EC$('.add-product-checked:checked').length === 0) {
18448                return true;
18449            }
18450
18451            var aAddProduct = CAFE24.UTIL.parseJSON(add_option_data);
18452            var aItemCode = new Array();
18453            var bCheckValidate = true;
18454            EC$('.add-product-checked:checked').each(function() {
18455                if (bCheckValidate === false) {
18456                    return false;
18457                }
18458                var iProductNum = EC$(this).attr('product-no');
18459                var iQuantity = EC$('#add-product-quantity-'+iProductNum).val();
18460                var aData = aAddProduct[iProductNum];
18461                if (aData.item_code === undefined) {
18462                    if (aData.option_type === 'T') {
18463                        if (aData.item_listing_type === 'S') {
18464                            var aOptionValue = new Array();
18465                            EC$('[id^="addproduct_option_id_'+iProductNum+'"]').each(function() {
18466                                aOptionValue.push(EC$(this).val());
18467                            });
18468                            if (ITEM.isOptionSelected(aOptionValue) === true) {
18469                                sOptionValue = aOptionValue.join('#$%');
18470                                aItemCode.push([CAFE24.UTIL.parseJSON(aData.option_value_mapper)[sOptionValue],iQuantity]);
18471                            } else {
18472                                bCheckValidate = false;
18473                                alert(__('필수 옵션을 선택해주세요.'));
18474                                return false;
18475                            }
18476                        } else {
18477                            var $eItemSelectbox = EC$('[name="addproduct_option_name_'+iProductNum+'"]');
18478
18479                            if (ITEM.isOptionSelected($eItemSelectbox.val()) === true) {
18480                                aItemCode.push([$eItemSelectbox.val(),iQuantity]);
18481                            } else {
18482                                bCheckValidate = false;
18483                                $eItemSelectbox.focus();
18484                                alert(__('필수 옵션을 선택해주세요.'));
18485                                return false;
18486                            }
18487                        }
18488                    } else if (Olnk.isLinkageType(sOptionType) === true) {
18489                        EC$('[id^="addproduct_option_id_'+iProductNum+'"]').each(function() {
18490                            alert(EC$(this).val());
18491                            if (EC$(this).prop('required') === true && ITEM.isOptionSelected(EC$(this).val()) === false) {
18492                                bCheckValidate = false;
18493                                EC$(this).focus();
18494                                alert(__('필수 옵션을 선택해주세요.'));
18495                                return false;
18496                            }
18497
18498                            if (ITEM.isOptionSelected(EC$(this).val()) === true) {
18499                                aItemCode.push([EC$(this).val(),iQuantity]);
18500                            }
18501                        });
18502                    } else {
18503                        EC$('[id^="addproduct_option_id_'+iProductNum+'"]').each(function() {
18504                            if (EC$(this).prop('required') === true && ITEM.isOptionSelected(EC$(this).val()) === false) {
18505                                bCheckValidate = false;
18506                                EC$(this).focus();
18507                                alert(__('필수 옵션을 선택해주세요.'));
18508                                return false;
18509                            }
18510                            if (ITEM.isOptionSelected(EC$(this).val()) === true) {
18511                                aItemCode.push([EC$(this).val(),iQuantity]);
18512                            }
18513                        });
18514                    }
18515                } else {
18516                    aItemCode.push([aData.item_code,iQuantity]);
18517                }
18518            });
18519            if (bCheckValidate === false) {
18520                return false;
18521            }
18522            for (var x = 0; x < aItemCode.length; x++) {
18523                this.UTIL.appendHidden('relation_item[]', aItemCode[x][1]+'||'+aItemCode[x][0]);
18524            }
18525        },
18526        isValidRegularDelivery: function() // 정기 배송
18527        {
18528            if (PRODUCTSUBMIT.isRegularDelivery() === false) {
18529                return true;
18530            }
18531            if (EC_FRONT_JS_CONFIG_SHOP.bIsLogin === false) {
18532                alert(__('AVAILABLE.AFTER.LOGIN', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18533                btn_action_move_url('/member/login.html');
18534                return false;
18535            }
18536
18537            var sSubscriptionCycleValue = PRODUCTSUBMIT.getRegularSelectedValue();
18538
18539            if (sSubscriptionCycleValue === '') {
18540                alert(__('REGULAR.SHIPPING.CYCLE', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18541                return false;
18542            }
18543
18544            if (EC$('#EC_regular_count option:selected').val() === '') {
18545                alert(__('REGULAR.SHIPPING.COUNT', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18546                return false;
18547            }
18548
18549            return true;
18550        },
18551        isValidFunding: function()
18552        {
18553            if (PRODUCTSUBMIT.sType !== 'funding') {
18554                return true;
18555            }
18556
18557            if (CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isSelectComposition() === false) {
18558                alert(__('PRODUCT.CONFIGURATION', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18559                return false;
18560            }
18561
18562            if (CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isItemSelected() === false) {
18563                alert(__('SELECT.REQUIRED.OPTION.001', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18564                return false;
18565            }
18566
18567            // 최소 주문 수량
18568            if (CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isValidQuantity() === false) {
18569                alert(__('APPLY.RESERVATION', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18570                return false;
18571            }
18572
18573            return true;
18574        },
18575        isExclusivePurchaseOnly: function()
18576        {
18577            if (PRODUCTSUBMIT.isExclusivePurchaseOnly() === false) {
18578                return true;
18579            }
18580            if (this.parent.sType === 2) {
18581                alert(__('EXCLUSIVE.PURCHASE.ONLY.CANNOT', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18582                return false;
18583            }
18584
18585            if (this.parent.sType === 1 && EC$('input[class^="option_add_box_"][name="basket_add_product[]"]').length > 0) {
18586                alert(__('EXCLUSIVE.PURCHASE.ONLY.BUY', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18587                return false;
18588            }
18589            return true;
18590        },
18591        /**
18592         * 예약상품 데이터 검증
18593         * @returns bool 예약상품 검증 통과할경우 true
18594         */
18595        isValidReservationItemData: function()
18596        {
18597            return true;
18598        },
18599        isValidCulturalTax: function()
18600        {
18601            // 본상품 : 문화비와 정기배송 둘다 대상이면 차단
18602            if (PRODUCTSUBMIT.isValidCulturalTax() === true && PRODUCTSUBMIT.isRegularDelivery() === true) {
18603                alert(__('THROUGH.REGULAR.DELIVERY', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18604                return false;
18605            }
18606
18607            // 추가구성상품 : 본품과 추가구성상품이 문화비와 일반이 섞여 있으면 차단
18608            if (EC$('input[class^="option_add_box_"][name="basket_add_product[]"]').length > 0) {
18609                const sCulturalTaxType = SHOP_FRONT_PRODUCT_CULTURAL_TAX.getIsCulturalTax();
18610                const isConsistent = EC$('input[class^="option_add_box_"][name="basket_add_product[]"]').toArray().every(input => {
18611                    const addProductValue = $(input).val();
18612                    const addProductArray = addProductValue.split('||');
18613                    const sAddCulturalTaxType = addProductArray.length > 12 ? addProductArray[12] : '';
18614                    if (sAddCulturalTaxType === 'T' || sAddCulturalTaxType === 'F') {
18615                        return sCulturalTaxType === sAddCulturalTaxType;
18616                    }
18617                    return true; // 'T' 또는 'F'가 아닐 경우 검증하지 않음
18618                });
18619
18620                if (!isConsistent) {
18621                    alert(__('CANNOT.PURCHASED.TOGETHER', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18622                    return false;
18623                }
18624            }
18625
18626            return true;
18627        }
18628    },
18629    getRegularSelectedValue : function()
18630    {
18631        var sSubscriptionCycleAttrName = '.EC_regular_cycle_count';
18632        if (EC$('.EC_regular_cycle_count').prop('type') === 'select-one') {
18633            sSubscriptionCycleAttrName = '.EC_regular_cycle_count > option:selected';
18634        } else if (EC$('.EC_regular_cycle_count:checked').length > 0) {
18635            sSubscriptionCycleAttrName = '.EC_regular_cycle_count:checked';
18636        }
18637
18638        var sSubscriptionCycleValue = '';
18639        if (EC$('.EC_regular_cycle_count').length === 0) {
18640            sSubscriptionCycleValue = parent.EC$(sSubscriptionCycleAttrName).val();
18641        } else {
18642            sSubscriptionCycleValue = EC$(sSubscriptionCycleAttrName).val();
18643        }
18644
18645        return sSubscriptionCycleValue;
18646    },
18647    getRegularCountSelectedValue : function()
18648    {
18649        var sSubscriptionCountAttrName = '.EC_regular_count';
18650        if (EC$('.EC_regular_count').prop('type') === 'select-one') {
18651            sSubscriptionCountAttrName = '.EC_regular_count > option:selected';
18652        } else if (EC$('.EC_regular_count:checked').length > 0) {
18653            sSubscriptionCountAttrName = '.EC_regular_count:checked';
18654        }
18655
18656        var sSubscriptionCountValue = '';
18657        if (EC$('.EC_regular_count').length === 0) {
18658            sSubscriptionCountValue = parent.EC$(sSubscriptionCountAttrName).val();
18659        } else {
18660            sSubscriptionCountValue = EC$(sSubscriptionCountAttrName).val();
18661        }
18662
18663        return sSubscriptionCountValue;
18664    },
18665    UTIL: {
18666        oData: {},
18667        initialize: function(oParent)
18668        {
18669            this.parent = oParent;
18670            return this;
18671        },
18672        appendHidden: function(mParam)
18673        {
18674            if (typeof(mParam) === 'string' && arguments.length === 2) {
18675                this.setHidden(arguments[0], arguments[1]);
18676            }
18677            if (typeof(mParam) === 'object') {
18678                for (var sName in mParam) {
18679                    if (Array.isArray(mParam[sName]) === true) {
18680                        EC$.each(mParam[sName], function(iIndex, mValue) {
18681                            PRODUCTSUBMIT.UTIL.setHidden(sName+'[]', mValue);
18682                        });
18683                        continue;
18684                    }
18685                    this.setHidden(sName, mParam[sName]);
18686                }
18687            }
18688        },
18689        prependHidden: function(mParam)
18690        {
18691            if (typeof(mParam) === 'string' && arguments.length === 2) {
18692                this.setHidden(arguments[0], arguments[1], 'prepend');
18693            }
18694            if (typeof(mParam) === 'object') {
18695                for (var sName in mParam) {
18696                    if (Array.isArray(mParam[sName]) === true) {
18697                        EC$.each(mParam[sName], function(iIndex, mValue) {
18698                            PRODUCTSUBMIT.UTIL.setHidden(sName+'[]', mValue, 'prepend');
18699                        });
18700                        continue;
18701                    }
18702                    this.setHidden(sName, mParam[sName], 'prepend');
18703                }
18704            }
18705        },
18706        setHidden: function(sName, sValue, sAppendType)
18707        {
18708            //ECHOSTING-9736
18709            if (typeof(sValue) === "string" && (sName == "option_add[]" || sName.indexOf("item_option_add") === 0)) {
18710                 sValue = sValue.replace(/'/g, '\\&#039;');
18711            }
18712
18713            // 타입이 string 일때 연산시 단일 따움표 " ' " 문자를 " ` " 액센트 문자로 치환하여 깨짐을 방지
18714            var oAttribute = {
18715                'name': sName,
18716                'type': 'hidden',
18717                'class': 'basket-hidden'
18718            };
18719            if (sAppendType === 'prepend') {
18720                this.parent.oForm.prepend(EC$('<input>').attr(oAttribute).val(sValue));
18721
18722            } else {
18723                this.parent.oForm.append(EC$('<input>').attr(oAttribute).val(sValue));
18724
18725            }
18726        },
18727        setData: function(sKey, mValue)
18728        {
18729            this.oData[sKey] = mValue;
18730            return true;
18731        },
18732        getData: function(sKey)
18733        {
18734            return this.oData[sKey];
18735        }
18736    },
18737    DEBUG: {
18738        aMessage: [],
18739        initialize: function(oParent)
18740        {
18741            this.aMessage = [];
18742            this.parent = oParent;
18743            this.bIsDebugConsoleOut = this.parent.bIsDebugConsoleOut;
18744            return this;
18745        },
18746        setInfo: function()
18747        {
18748            if (this.bIsDebugConsoleOut === false) {
18749                return;
18750            }
18751            if (window.console) {
18752                var aMessage = [];
18753                for (var i = 0; i < arguments.length; i++) {
18754                    aMessage.push(arguments[i]);
18755                }
18756                console.info(aMessage.join(''));
18757            }
18758        },
18759        setMessage: function(sMessage)
18760        {
18761            this.aMessage.push(sMessage);
18762            this.setConsoleDebug();
18763            throw 'USER_DEFINED_ERROR';
18764        },
18765        setConsoleDebug: function()
18766        {
18767            if (this.bIsDebugConsoleOut === false) {
18768                return;
18769            }
18770            if (window.console) {
18771                console.warn(this.aMessage.join('\n'));
18772            }
18773        },
18774        messageOut: function(mError, sLabel)
18775        {
18776            if (this.bIsDebugConsoleOut === true && mError !== 'USER_DEFINED_ERROR') {
18777                console.error(mError);
18778            }
18779
18780            if (typeof EC_JET === 'object' && EC_JET.hasOwnProperty('message') === true) {
18781                var oErrorMessage = mError === 'USER_DEFINED_ERROR' ? this.aMessage : mError;
18782                EC_JET.message(oErrorMessage, sLabel);
18783            }
18784
18785            return false;
18786        }
18787    },
18788    setLoginMovePage: function(sUrl , sMessage)
18789    {
18790        if (sMessage !== '') {
18791            alert(sMessage);
18792        }
18793        if (typeof(sUrl) === 'undefined') {
18794            sUrl = '/member/login.html';
18795        }
18796        var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
18797        sUrl += '?returnUrl=' + encodeURIComponent(oTarget.location.pathname + oTarget.location.search);
18798        oTarget.location.href = sUrl;
18799    },
18800    isRegularDelivery : function()
18801    {
18802        if (EC_FRONT_JS_CONFIG_SHOP.bRegularConfig === false) {
18803            return false;
18804        }
18805        if (EC$('.EC_regular_delivery:checked').length === 0 || EC$('.EC_regular_delivery:checked').val() === 'F') {
18806            return false;
18807        }
18808
18809        return true;
18810    },
18811
18812    sendLoginPage : function(iPrdNo, sBuyLimitType) {
18813        var sLoginPage = '/member/login.html?returnUrl=' + encodeURIComponent(location.pathname + location.search);
18814        var oTarget = document;
18815        // 분류페이지에서 옵션선택레이어에서 선택시에는 부모페이지를 로그인페이지로 이동
18816        if (parent.$('#capp-shop-new-product-optionselect-layer').length > 0 || parent.$('#modalContent').length > 0) {
18817            oTarget = parent.top.document;
18818        }
18819
18820        if (typeof iPrdNo !== 'undefined') {
18821            EC$.ajax({
18822                url: '/exec/front/Shop/ApiProductPurchase',
18823                type: 'get',
18824                async: false,
18825                data: {
18826                    'product_no': iPrdNo
18827                },
18828                dataType: 'json',
18829                success: function(oReturn) {
18830                    if (oReturn.bIsSuccess === true) {
18831                        //상품정보가 변경되어 구매할 수 없습니다.
18832                        alert(sprintf(__('INFORMATION.CHANGED', 'SHOP.JS.FRONT.NEW.PRODUCT.SET'), ''));
18833                        return;
18834                    }
18835
18836                    alert(oReturn.sMessage);
18837                    if (oReturn.bReplaceLoginPage === true) {
18838                        oTarget.location.href = sLoginPage;
18839                    }
18840                }
18841            });
18842            return;
18843        } else if (typeof iProductNo !== 'undefined') {
18844            if (typeof CAFE24.FRONT_JS_CONFIG_SHOP  !== 'undefined' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('aProductPurchaseInfo_' + iProductNo) === true) {
18845                var oPurchaseInfo = CAFE24.FRONT_JS_CONFIG_SHOP['aProductPurchaseInfo_' + iProductNo];
18846                alert(oPurchaseInfo.sMessage);
18847                if (oPurchaseInfo.bReplaceLoginPage === true) {
18848                    oTarget.location.href = sLoginPage;
18849                }
18850                return;
18851            }
18852        }
18853
18854        sMsg = __('CAN.PURCHASE.GROUP', 'GLOBAL.BUY.LIMIT');
18855        if (sBuyLimitType == 'O' || sBuyLimitType == 'D') {
18856            sMsg = __('CAN.PURCHASE.MEMBER.USE', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION');
18857        }
18858        alert(sMsg);
18859    },
18860    isExclusivePurchaseOnly: function()
18861    {
18862        if (EC_FRONT_JS_CONFIG_SHOP.bIsExclusivePurchaseOnly === false) {
18863            return false;
18864        }
18865        return true;
18866    },
18867    isValidCulturalTax: function()
18868    {
18869        return SHOP_FRONT_PRODUCT_CULTURAL_TAX.getIsCulturalTax() === 'T';
18870    }
18871};
18872
18873// 상품 옵션 id
18874var product_option_id = 'product_option_id';
18875
18876// 추가옵션 id
18877var add_option_id = 'add_option_';
18878
18879// 선택된 상품만 주문하기
18880var sIsPrdOverride = 'F';
18881
18882//모바일로 접속했는지
18883var bIsMobile = false;
18884
18885//분리형 세트상품의 구성상품(품절)에서 SMS 재입고 알림 팝업 호출
18886function set_sms_restock(iProductNo) {
18887    if (typeof(iProductNo) === 'undefined') {
18888        return;
18889    }
18890
18891    // 모바일 접속 및 레이어 팝업 여부 확인
18892    if (typeof(EC_SHOP_FRONT_PRODUCT_SMS_RESTOCK_LAYER) !== 'undefined') {
18893        var sParam = 'product_no=' + iProductNo;
18894        if (EC_SHOP_FRONT_PRODUCT_SMS_RESTOCK_LAYER.createSmsRestockLayerDisplayResult(sParam) === true) {
18895            return;
18896        }
18897    }
18898
18899    window.open('/product/sms_restock.html?product_no=' + iProductNo, 'sms_restock', 200, 100, 459, 490);
18900}
18901
18902// 예약 주문 체크
18903var STOCKTAKINGCHECKRESERVE = {
18904    checkReserve: function()
18905    {
18906        var bIsReserveStatus = EC$('.option_box_id').filter('[data-item-reserved="R"]').length > 0;
18907        // 예약 주문이 있는경우
18908        if (bIsReserveStatus === true) {
18909            alert(__('ITEMS.MAY.SHIPPED', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
18910        }
18911        return false;
18912    }
18913};
18914
18915
18916/**
18917 * sType - 1:바로구매, 2:장바구니,naver_checkout:네이버 페이 form.submit - 바로구매, 장바구니, 관심상품
18918 *         app: app 에서 필요한 장바구니데이터 "aAppBasketInsertItem" return
18919 * TODO 바로구매 - 장바구니에 넣으면서 주문한 상품 하나만 주문하기
18920 *
18921 * @param string sAction action url
18922 */
18923function product_submit(sType, sAction, oObj, aInfo, defer)
18924{
18925    // jwt에서 중복호출이 되어야하여 패스할 수 있도록 예외처리
18926    if (sType !== 'direct_buy' && unsetOnclikAction(oObj) === false) {
18927        return;
18928    }
18929    PRODUCTSUBMIT.initialize(sType, sAction, oObj, aInfo);
18930
18931    if (PRODUCTSUBMIT.isValidRequest() === false) {
18932        setTimeout(function() {
18933            setOnclikAction();
18934        }, 1000);
18935        return {
18936            'result': false,
18937            'code': 422,
18938            'message': 'fail valid request'
18939        }
18940    }
18941
18942    if (PRODUCTSUBMIT.setBasketForm() === false) {
18943        setTimeout(function() {
18944            setOnclikAction();
18945        }, 1000);
18946        return {
18947            'result': false,
18948            'code': 422,
18949            'message': 'fail basket form'
18950        }
18951    }
18952
18953    PRODUCTSUBMIT.setBasketAjax(defer);
18954
18955    setTimeout(function() {
18956        setOnclikAction();
18957    }, 1000);
18958
18959    return;
18960}
18961
18962/**
18963 * 선택한상품만 주문하기
18964 *
18965 * @param string sOptionParam 옵션 파람값
18966 * @param int iProductNo 상품번호
18967 * @param string sSelectedItemByEtype 상품연동형의 경우 입력되는 선택된옵션 json 데이터
18968 */
18969function selectbuy_action(sOptionParam, iProductNo, sSelectedItemByEtype)
18970{
18971    var sAddParam = '';
18972    if (typeof sSelectedItemByEtype !== 'undefined' && sSelectedItemByEtype != '') {
18973        sAddParam = '&' + sSelectedItemByEtype;
18974    }
18975
18976    var sChRefDataParam = '';
18977    if (CAFE24.getChRefData()) {
18978        sChRefDataParam = '&ch_ref=' + CAFE24.getChRefData();
18979    }
18980
18981    var sUrl = '/exec/front/order/basket/?command=select_prdcnt&product_no=' + iProductNo + '&option_type=' + (window['option_type'] || '') + sOptionParam + sAddParam + sChRefDataParam;
18982
18983    EC$.ajax(
18984    {
18985        url: sUrl,
18986        dataType: 'json',
18987        async: false,
18988        success: function(data)
18989        {
18990            if (data.result > 0) {
18991                //1+N상품이라면
18992                if (typeof CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE !== 'undefined' && CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.oBundleConfig.hasOwnProperty(iProductNo) === true) {
18993                    sIsPrdOverride = 'F';
18994                } else {
18995                    if (confirm(CAFE24.SHOP_FRONT_NEW_PRODUCT_COMMON.getOverridePurchaseMessage(data.result)) === false) {
18996                        sIsPrdOverride = 'T';
18997                    }
18998                }
18999            }
19000        }
19001    });
19002}
19003var sOnclick = null;
19004var oThisSelector = null;
19005
19006/**
19007 * 중복클릭 방지를 위한 onclick off 처리
19008 * @param object oObject 클릭된 오브젝트
19009 * @returns {boolean}
19010 */
19011function unsetOnclikAction(oObject)
19012{
19013    oSelector = EC$(oObject);
19014    // 중복클릭 제어
19015    if (sOnclick !== null) {
19016        //alert(__('처리중입니다. 잠시만 기다려주세요.'));
19017        return false;
19018    }
19019    // onclick 속성 제거후 임시 저장
19020    sOnclick = oSelector.attr('onclick');
19021    oSelector.attr('onclick', '');
19022    oThisSelector = oSelector;
19023
19024    return true;
19025
19026}
19027/**
19028 * 중복클릭 onclick 복구
19029 */
19030function setOnclikAction()
19031{
19032    // 제거된 onclick 속성 복구
19033    if (sOnclick !== null) {
19034        oThisSelector.attr('onclick', sOnclick);
19035    }
19036    sOnclick = null;
19037    oThisSelector = null;
19038}
19039/**
19040 * 장바구니 담기(카테고리)
19041 *
19042 * @param int iProductNo 상품번호
19043 * @param int iCategoryNo 카테고리 번호
19044 * @param int iDisplayGroup display_group
19045 * @param string sBasketType 무이자 설정(A0000:일반, A0001:무이자)
19046 * @param string iQuantity 주문수량
19047 * @param string sItemCode 아이템코드
19048 * @param string sDelvType 배송타입
19049 */
19050function category_add_basket(iProductNo, iCategoryNo, iDisplayGroup, sBasketType, bList, iQuantity, sItemCode, sDelvType, sProductMaxType, sProductMax)
19051{
19052    if (unsetOnclikAction(this.event.target) === false) {
19053        return;
19054    }
19055    if (iQuantity == undefined) {
19056        iQuantity = 1;
19057    }
19058    if (typeof EC_FRONT_JS_CONFIG_SHOP  !== 'undefined' && typeof EC_FRONT_JS_CONFIG_SHOP.aExclusivePurchase !== 'undefined' && EC_FRONT_JS_CONFIG_SHOP.aExclusivePurchase !== null) {
19059        if (EC_FRONT_JS_CONFIG_SHOP.aExclusivePurchase.hasOwnProperty(iProductNo) === true) {
19060            if (EC_FRONT_JS_CONFIG_SHOP.aExclusivePurchase[iProductNo]) {
19061                alert(__('EXCLUSIVE.PURCHASE.ONLY.CANNOT', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION'));
19062                setTimeout(function() {
19063                    setOnclikAction();
19064                }, 1000);
19065                return false;
19066            }
19067        }
19068    }
19069    if (bList == true) {
19070        try {
19071            if (typeof EC_ListAction === 'object') {
19072                EC_ListAction.getOptionSelect(iProductNo, iCategoryNo, iDisplayGroup, sBasketType);
19073            }
19074        } catch (e) {
19075            PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.category_add_basket');
19076            alert(__('장바구니에 담을 수 없습니다.'));
19077            setTimeout(function() {
19078                setOnclikAction();
19079            }, 1000);
19080            return false;
19081        }
19082    } else {
19083        var sAction = '/exec/front/order/basket/';
19084        var sData = 'command=add&quantity=' + iQuantity + '&product_no=' + iProductNo + '&main_cate_no=' + iCategoryNo + '&display_group='
19085            + iDisplayGroup + '&basket_type=' + sBasketType + '&delvtype=' + sDelvType + '&product_max_type=' + sProductMaxType + '&product_max=' + sProductMax
19086            + '&iQuantity=' + iQuantity + '&sFrom=category';
19087        // 장바구니 위시리스트인지 여부
19088        if (typeof (basket_page_flag) !== 'undefined' && basket_page_flag == 'T') {
19089            sData = sData + '&basket_page_flag=' + basket_page_flag;
19090        }
19091
19092        action_basket(2, 'category', sAction, sData, sBasketType);
19093    }
19094
19095}
19096
19097/**
19098 * 구매하기
19099 *
19100 * @param int iProductNo 상품번호
19101 * @param int iCategoryNo 카테고리 번호
19102 * @param int iDisplayGroup display_group
19103 * @param string sBasketType 무이자 설정(A0000:일반, A0001:무이자)
19104 * @param string iQuantity 주문수량
19105 */
19106function add_order(iProductNo, iCategoryNo, iDisplayGroup, sBasketType, iQuantity)
19107{
19108    if (iQuantity == undefined) {
19109        iQuantity = 1;
19110    }
19111
19112    var sAction = '/exec/front/order/basket/';
19113    var sData = 'command=add&quantity=' + iQuantity + '&product_no=' + iProductNo + '&main_cate_no=' + iCategoryNo + '&display_group='
19114            + iDisplayGroup + '&basket_type=' + sBasketType;
19115
19116    action_basket(1, 'wishlist', sAction, sData, sBasketType);
19117}
19118
19119/**
19120 * 레이어 생성
19121 *
19122 * @param layerId
19123 * @param sHtml
19124 */
19125function create_layer(layerId, sHtml, oTarget)
19126{
19127    let sChRefData = CAFE24.getChRefData();
19128    //아이프레임일때만 상위객체에 레이어생성
19129    if (oTarget === parent) {
19130        oTarget.EC$('#' + layerId).remove();
19131        oTarget.EC$('body').append(EC$('<div id="' + layerId + '" style="position:absolute; z-index:10001;"></div>'));
19132        oTarget.EC$('#' + layerId).html(sHtml);
19133        oTarget.EC$('#' + layerId).show();
19134
19135        parent.ReferenceCurrencyPrice.init();
19136        //옵션선택 레이어 프레임일 경우 그대로 둘경우 영역에대해 클릭이 안되는부분때문에 삭제처리
19137        if (typeof(bIsOptionSelectFrame) !== 'undefined' && bIsOptionSelectFrame === true) {
19138            parent.CAPP_SHOP_NEW_PRODUCT_OPTIONSELECT.closeOptionCommon();
19139        }
19140    } else {
19141        EC$('#' + layerId).remove();
19142        EC$('<div id="' + layerId + '"></div>').appendTo('body');
19143        EC$('#' + layerId).html(sHtml);
19144        EC$('#' + layerId).show();
19145
19146        ReferenceCurrencyPrice.init();
19147    }
19148
19149    let sBasketUrl = '/order/basket.html?delvtype=' + delvtype;
19150    if (sChRefData !== undefined) {
19151        sBasketUrl += "&ch_ref=" + sChRefData;
19152    }
19153    // set delvtype to basket
19154    try {
19155        EC$(".xans-product-basketadd").find("a[href^='/order/basket.html']").attr("href", sBasketUrl);
19156    } catch (e) {}
19157    try {
19158        EC$(".xans-order-layerbasket").find("a[href='/order/basket.html']").attr("href", sBasketUrl);
19159    } catch (e) {}
19160}
19161
19162/**
19163 * 레이어 위치 조정
19164 *
19165 * @param layerId
19166 */
19167function position_layer(layerId)
19168{
19169    var obj = EC$('#' + layerId);
19170
19171    var x = 0;
19172    var y = 0;
19173    try {
19174        var hWd = parseInt(document.body.clientWidth / 2 + EC$(window).scrollLeft());
19175        var hHt = parseInt(document.body.clientHeight / 2 + EC$(window).scrollTop() / 2);
19176        var hBW = parseInt(obj.width()) / 2;
19177        var hBH = parseInt(hHt - EC$(window).scrollTop());
19178
19179        x = hWd - hBW;
19180        if (x < 0) x = 0;
19181        y = hHt - hBH;
19182        if (y < 0) y = 0;
19183
19184    } catch (e) {}
19185
19186    obj.css(
19187    {
19188        position: 'absolute',
19189        display: 'block',
19190        top: y + "px",
19191        left: x + "px"
19192    });
19193
19194}
19195
19196
19197// 장바구니 담기 처리중인지 체크 - (ECHOSTING-85853, 2013.05.21 by wcchoi)
19198var bIsRunningAddBasket = false;
19199
19200var aAppBasketInsertItem = null; // 장바구니 insert 동작 후, 해당 장바구니 데이터
19201var aCheckoutBasketItem;
19202var bIsCheckoutBasketSet = false; // 체크아웃 결제수단 (kakaopay...) 장바구니 세팅 여부
19203
19204/**
19205 * 장바구니/구매 호출
19206 *
19207 * @param sType
19208 * @param sGroup
19209 * @param sAction
19210 * @param sParam
19211 * @param aBasketType
19212 * @param bNonDuplicateChk
19213 */
19214
19215function action_basket(sType, sGroup, sAction, sParam, sBasketType, bNonDuplicateChk, defer)
19216{
19217    if (bIsRunningAddBasket) {
19218        //alert(__('처리중입니다. 잠시만 기다려주세요.'));
19219        return;
19220    }
19221
19222    // 장바구니 담기에 대해서만 처리
19223    // 중복 체크 안함 이 true가 아닐경우(false나 null)에만 중복체크
19224    if (sType == 2 && bNonDuplicateChk != true) {
19225        if (bIsRunningAddBasket === false) {
19226            bIsRunningAddBasket = true;
19227        }
19228    }
19229
19230    if (sType == 'sms_restock') {
19231        action_sms_restock(sParam);
19232        return;
19233    }
19234
19235    if (sType == 'email_restock') {
19236        action_email_restock();
19237        return;
19238    }
19239
19240    if (sType == 2 && CAFE24.SHOP_FRONT_BASKET_VALID.isBasketProductDuplicateValid(sParam) === false) {
19241        bIsRunningAddBasket = false;
19242        setTimeout(function() {
19243            setOnclikAction();
19244        }, 1000);
19245        return false;
19246    }
19247
19248    // UA 초기화 이슈로 동기호출로 변경 (ECHOSTING-446124)
19249    EC$.ajax({
19250        type: 'POST',
19251        url: sAction,
19252        data: sParam,
19253        async: false,
19254        success: function(data) {
19255
19256            // 바로구매(direct_buy)는 채널 UI 에서도 일반 결과 핸들러(basket_result_action)로 처리한다. (ECHOSTING-625233)
19257            // 채널 결과 핸들러(basket_result_action_channel)에는 directbuy 의 주문서 갱신(setOrderForm→formdirectbuy) 분기가 없어
19258            // 전용디자인 바로구매에서 상품/결제금액이 렌더되지 않기 때문이다.
19259            if (CAFE24.checkChannelUI() === true && sType !== 'direct_buy') {
19260                if (sType == 1) {
19261                    CAFE24_CHANNEL_MANAGE.do(function(options) {
19262                        var defaults = {
19263                            _sType: sType,
19264                            _sGroup: sGroup,
19265                            _data: data,
19266                            _sBasketType: sBasketType,
19267                            _sParam: sParam,
19268                            _defer: defer
19269                        };
19270                        var settings = EC$.extend({}, defaults, options);
19271
19272                        // settings 객체를 사용하여 작업 수행
19273                        basket_result_action_channel(settings._sType, settings._sGroup, settings._data, settings._sBasketType, settings._sParam, settings._defer);
19274                    });
19275
19276                    /*CAFE24_CHANNEL_MANAGE.do(function(_sType = sType, _sGroup = sGroup, _data = data, _sBasketType = sBasketType, _sParam = sParam, _defer = defer) {
19277                        basket_result_action_channel(_sType, _sGroup, _data, _sBasketType, _sParam, _defer);
19278                    });*/
19279                } else {
19280                    basket_result_action_channel(sType, sGroup, data, sBasketType, sParam, defer);
19281                }
19282            } else {
19283                basket_result_action(sType, sGroup, data, sBasketType, sParam, defer);
19284            }
19285
19286            bIsRunningAddBasket = false; // 장바구니 담기 처리 완료
19287
19288            setTimeout(function() {
19289                setOnclikAction();
19290            }, 1000);
19291
19292            // 세션스토리지 BasketProduct 캐시 삭제
19293            EC$(function() {
19294                try {
19295                    CAPP_ASYNC_METHODS.BasketProduct.removeCache();
19296                } catch (e) {
19297                }
19298            });
19299        },
19300        dataType: 'json'
19301    });
19302
19303}
19304
19305/**
19306 * 리스트나 상세에서 장바구니 이후의 액션을 처리하고 싶을 경우 이변수를 파라미터로 지정해줌
19307 */
19308var sProductLink = null;
19309/**
19310 * 장바구니 결과 처리
19311 *
19312 * @param sType
19313 * @param sGroup
19314 * @param aData
19315 * @param sBasketType
19316 * @param sParam
19317 */
19318function basket_result_action(sType, sGroup, aData, sBasketType, sParam, defer)
19319{
19320    if (aData == null) {
19321        return;
19322    }
19323
19324    var sHtml = '';
19325    var bOpener = false;
19326    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19327    var bIsProgressLink = true;
19328
19329    var oCheckZoomPopUp = {
19330        isPopUp: function()
19331        {
19332            var bIsPopup = false;
19333            if (bIsProgressLink === true || (typeof(sIsPopUpWindow) !== "undefined" && sIsPopUpWindow === "T")) {
19334                if (CAPP_SHOP_FRONT_COMMON_UTIL.isPopupFromThisShopFront() === true) {
19335                    bIsPopup = true;
19336                }
19337            }
19338            return bIsPopup;
19339        }
19340    };
19341
19342    //var oOpener = findMainFrame();
19343    //var sLocation = location;
19344    var bBuyLayer = false;
19345
19346    // 쿠폰적용 가능상품 팝업 -> 상품명 클릭하여 상품상세 진입 -> 바로 구매 시,
19347    // 쿠폰적용 가능상품 팝업이 열려있으면 주문서 페이지로 이동되지 않고, 창이 닫이는 이슈 처리(ECHOSTING-266906)
19348    if (sType == 1 && window.opener !== null && oTarget.couponPopupClose !== undefined) {
19349        bOpener = true;
19350    }
19351
19352    if (aData.result >= 0) {
19353        try {
19354            bBuyLayer = ITEM.setBodyOverFlow(true);
19355        } catch (e) {
19356            PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.basket_result_action');
19357        }
19358
19359        // - insert 된 장바구니 데이터 : aAppBasketInsertItem
19360        if (sType == 'app') {
19361            aAppBasketInsertItem = aData['aBasketProduct'];
19362            defer(aAppBasketInsertItem);
19363            return;
19364        }
19365
19366        if (sType == 'checkout') {
19367            aCheckoutBasketItem = aData['aBasketProduct'];
19368            bIsCheckoutBasketSet = true;
19369            return;
19370        }
19371
19372        // 네이버 페이
19373        if (sType == 'naver_checkout') {
19374            var sUrl = '/exec/front/order/navercheckout';
19375
19376            // inflow param from naver common JS to Checkout Service
19377            try {
19378                if (typeof(wcs) === 'object') {
19379                    var inflowParam = wcs.getMileageInfo();
19380                    if (inflowParam != false) {
19381                        sUrl = sUrl + '?naver_inflow_param=' + inflowParam;
19382                    }
19383                }
19384            } catch (e) {}
19385
19386            // ECHOSTING-627626 CA_JS 광고 폼 데이터 동봉 위해 POST 폼 제출로 전환
19387            CAPP_SHOP_FRONT_COMMON_UTIL.submitNaverCheckout(sUrl, oTarget, (is_order_page == 'N' && bIsMobile == false));
19388            return false;
19389        }
19390
19391        // 배송유형
19392        var sDelvType = '';
19393        if (typeof(delvtype) !== 'undefined') {
19394            if (typeof(delvtype) === 'object') {
19395                sDelvType = EC$(delvtype).val();
19396            } else {
19397                sDelvType = delvtype;
19398            }
19399        } else if (aData.sDelvType != null) {
19400            sDelvType = aData.sDelvType;
19401        }
19402
19403        if (sType == 1 || sType === 'funding' || sType === 'simple_pay' || sType === 'reservation') { // 바로구매하기
19404            var sSimplePayType = '';
19405            if (sType === 'simple_pay' && aData.sPaymethod.length > 0) {
19406                sSimplePayType = '&paymethod=' + aData.sPaymethod;
19407            }
19408
19409            if (aData.isLogin == 'T') { // 회원
19410                var sUrl = "/order/orderform.html?basket_type=" + sBasketType + "&delvtype=" + sDelvType + sSimplePayType;
19411
19412                // URL 에 ch_ref 파라미터 추가
19413                if (CAFE24.hasChRef()) {
19414                    // 이미 ch_ref가 포함되어 있는지 확인
19415                    var urlObj = new URL(sUrl, window.location.origin);
19416                    if (!urlObj.searchParams.has('ch_ref')) {
19417                        sUrl = CAFE24.attachChRef(sUrl);
19418                    }
19419                }
19420
19421                if (bOpener === true) {
19422                    // 쿠폰적용 가능상품 팝업이 열려있을 때, 팝업이 아닌 현재 페이지(상품상세)가 주문서 페이지로 이동되도록 처리(ECHOSTING-266906)
19423                    self.location.href = sUrl;
19424                } else {
19425                    oTarget.location.href = sUrl;
19426                }
19427            } else { // 비회원
19428                sUrl = '/member/login.html?noMember=1&returnUrl=' + encodeURIComponent('/order/orderform.html?basket_type=' + sBasketType + "&delvtype=" + sDelvType + sSimplePayType);
19429                sUrl += '&delvtype=' + sDelvType;
19430
19431                // URL 에 ch_ref 파라미터 추가
19432                if (CAFE24.hasChRef()) {
19433                    // 이미 ch_ref가 포함되어 있는지 확인
19434                    var urlObj = new URL(sUrl, window.location.origin);
19435                    if (!urlObj.searchParams.has('ch_ref')) {
19436                        sUrl = CAFE24.attachChRef(sUrl);
19437                    }
19438                }
19439
19440                oTarget.location.href = sUrl;
19441            }
19442        } else if (sType === 'direct_buy') {
19443            CAFE24.SHOP_FRONT_ORDERFORM_DIRECTBUY.proc.setOrderForm(TotalAddSale.getDirectBuyParam());
19444            return;
19445        } else { // 장바구니담기
19446            var oData = CAFE24.PLUSAPP_BRIDGE.unserialize(sParam);
19447            CAFE24.PLUSAPP_BRIDGE.addBasket(oData);
19448            if (typeof CAFE24.KAKAO_PIXEL_BRIDGE !== 'undefined') {
19449                CAFE24.KAKAO_PIXEL_BRIDGE.addBasket(oData['product_no']);
19450            }
19451
19452            CAFE24.EC_SHOP_FRONT_BASKET_ACTION_EVENT.trigger(sParam, aData);
19453
19454            var oBasketEventData = CAFE24.EC_SHOP_FRONT_BASKET_ACTION_EVENT.getEventData();
19455            parent.postMessage({event_type: 'add_cart_success', basket_event_data: oBasketEventData}, window.parent.origin);
19456
19457            if (sGroup == 'detail') {
19458                if (mobileWeb === true) {
19459                    if (typeof (basket_page_flag) !== 'undefined' && basket_page_flag == 'T') {
19460                        oTarget.reload();
19461                        return;
19462                    }
19463                }
19464
19465                var oSearch = /basket.html/g;
19466                //레이어가 뜨는 설정이라면 페이지이동을 하지 않지만
19467                //레이어가 뜨어라고 확대보기팝업이라면 페이지 이동
19468
19469                if (typeof(aData.isDisplayBasket) !== "undefined" && aData.isDisplayBasket == 'T' && oSearch.test(window.location.pathname) == false) {
19470                    if ((typeof(aData.isDisplayLayerBasket) !== "undefined" && aData.isDisplayLayerBasket == 'T') && (typeof(aData.isBasketPopup) !== "undefined" && aData.isBasketPopup == 'T')) {
19471                        layer_basket2(sDelvType, oTarget);
19472                    } else {
19473                        //ECQAINT-14010 Merge이슈 : oTarget이 정상
19474                        layer_basket(sDelvType, oTarget);
19475                    }
19476
19477                    bIsProgressLink = false;
19478                }
19479
19480                //확인 레이어설정이 아니거나 확대보기 팝업페이지라면 페이지이동
19481                if (oCheckZoomPopUp.isPopUp() === true || bIsProgressLink === true) {
19482                    var sBasketUrl = "/order/basket.html?" + "&delvtype=" + sDelvType;
19483                    if (CAFE24.getChRefData() !== undefined) {
19484                        sBasketUrl += "&ch_ref=" + CAFE24.getChRefData();
19485                    }
19486                    oTarget.location.href = sBasketUrl;
19487                }
19488            } else {
19489                // from으로 위시리스트에서 요청한건지 판단.
19490                var bIsFromWishlist = false;
19491                if (typeof(aData.from) !== "undefined" && aData.from == "wishlist") {
19492                    bIsFromWishlist = true;
19493                }
19494
19495                // 장바구니 위시리스트인지 여부
19496                if (typeof (basket_page_flag) !== 'undefined' && basket_page_flag == 'T' || bIsFromWishlist == true) {
19497                    oTarget.reload();
19498                    return;
19499                }
19500                if (typeof(aData.isDisplayBasket) !== "undefined" && aData.isDisplayBasket === 'T') {
19501                    if ((typeof(aData.isDisplayLayerBasket) !== "undefined" && aData.isDisplayLayerBasket == 'T') && (typeof(aData.isBasketPopup) !== "undefined" && aData.isBasketPopup == 'T')) {
19502                        layer_basket2(sDelvType, oTarget);
19503                    } else {
19504                        layer_basket(sDelvType, oTarget);
19505                    }
19506                } else {
19507                    location.href = "/order/basket.html?" + "&delvtype=" + sDelvType;
19508                }
19509            }
19510        }
19511    } else {
19512        var msg = aData.alertMSG.replace('\\n', '\n');
19513
19514        // 디코딩 하기전에 이미 인코딩 된 '\n' 문자를 실제 개행문자로 변환
19515        // 목록에서 호출될 경우에는 인코딩 되지 않은 '\n' 문자 그대로 넘어오므로 추가 처리
19516        msg = msg.replace(/%5Cn|\\n/g, '%0A');
19517
19518        try {
19519            msg = decodeURIComponent(msg);
19520        } catch (err) {
19521            msg = unescape(msg);
19522        }
19523
19524        alert(msg);
19525
19526        if (aData.result == -111 && sProductLink !== null) {
19527            oTarget.href = '/product/detail.html?' + sProductLink;
19528        }
19529        if (aData.result == -101 || aData.result == -103) {
19530
19531
19532            sUrl = '/member/login.html?noMember=1&returnUrl=' + encodeURIComponent(oTarget.location.pathname + oTarget.location.search);
19533            oTarget.location.href = sUrl;
19534        }
19535
19536        if (aData.result == -113) {
19537            if (typeof(delvtype) !== 'undefined') {
19538                if (typeof(delvtype) === 'object') {
19539                    sDelvTypeForMove = EC$(delvtype).val();
19540                } else {
19541                    sDelvTypeForMove = delvtype;
19542                }
19543                oTarget.location.href = "/order/basket.html?" + "&delvtype=" + sDelvTypeForMove;
19544            } else {
19545                oTarget.location.href = "/order/basket.html";
19546            }
19547        }
19548    }
19549
19550    // ECHOSTING-130826 대응, 쿠폰적용상품 리스트에서 옵션상품(뉴옵션)담기 처리시, 화면이 자동으로 닫히지 않아 예외처리 추가
19551    if (oTarget.couponPopupClose !== undefined) {
19552        oTarget.couponPopupClose();
19553    }
19554    if (oCheckZoomPopUp.isPopUp() === true && bOpener === false) {
19555        self.close();
19556    } else {
19557        // ECHOSTING-130826 대응, 특정 화면에서 장바구니에 상품 담기 시 async 가 동작하지 않아,
19558        // 장바구니 담기처리 후처리 구간에 async 강제 실행추가
19559        // 쿠폰 적용 가능상품 리스트 에서 장바구니 담기시, 여기서 실행할 경우 js 오류가 발생하여, 함수 상단에 별도 처리 추가
19560        if (typeof(oTarget) !== 'undefined' && typeof(oTarget.CAPP_ASYNC_METHODS) !== 'undefined') {
19561            oTarget.CAPP_ASYNC_METHODS.init();
19562        } else {
19563            CAPP_ASYNC_METHODS.init();
19564        }
19565    }
19566}
19567
19568/**
19569 * 장바구니 결과 처리 by channel
19570 *
19571 * @param sType
19572 * @param sGroup
19573 * @param aData
19574 * @param sBasketType
19575 * @param sParam
19576 */
19577function basket_result_action_channel(sType, sGroup, aData, sBasketType, sParam, defer)
19578{
19579    if (aData == null) {
19580        return;
19581    }
19582
19583    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19584    var sBasketPageUrl = 'basket.html';
19585    var sOrderFormPageUrl = 'orderform.html';
19586
19587    if (aData.result >= 0) {
19588        // 배송유형
19589        var sDelvType = '';
19590        if (typeof(delvtype) !== 'undefined') {
19591            if (typeof(delvtype) === 'object') {
19592                sDelvType = EC$(delvtype).val();
19593            } else {
19594                sDelvType = delvtype;
19595            }
19596        } else if (aData.sDelvType != null) {
19597            sDelvType = aData.sDelvType;
19598        }
19599
19600        var sPrefixUrl = EC_ROUTE.isBaseDomain(location.href) === true ? '/shop' + EC_SDE_SHOP_NUM : '';
19601        if (sType == 1) { // 바로구매하기
19602            oTarget.location.href = CAFE24.attachChRef(sPrefixUrl + '/order/' + sOrderFormPageUrl + '?basket_type=' + sBasketType + '&delvtype=' + sDelvType);
19603        } else { // 장바구니담기
19604            CAFE24.EC_SHOP_FRONT_BASKET_ACTION_EVENT.trigger(sParam, aData);
19605            var oBasketEventData = CAFE24.EC_SHOP_FRONT_BASKET_ACTION_EVENT.getEventData();
19606            $("#basketAddLayer a.move").each(function() {
19607                $(this).attr("href", CAFE24.attachChRef(sPrefixUrl + '/order/' + sBasketPageUrl + '?delvtype=' + sDelvType));
19608            });
19609
19610            $('#basketAddLayer').addClass('on');
19611
19612            parent.postMessage({event_type: 'add_cart_success', basket_event_data: oBasketEventData}, window.parent.origin);
19613        }
19614    } else {
19615        var msg = aData.alertMSG.replace('\\n', '\n');
19616
19617        // 디코딩 하기전에 이미 인코딩 된 '\n' 문자를 실제 개행문자로 변환
19618        // 목록에서 호출될 경우에는 인코딩 되지 않은 '\n' 문자 그대로 넘어오므로 추가 처리
19619        msg = msg.replace(/%5Cn|\\n/g, '%0A');
19620
19621        try {
19622            msg = decodeURIComponent(msg);
19623        } catch (err) {
19624            msg = unescape(msg);
19625        }
19626
19627        alert(msg);
19628    }
19629}
19630
19631function layer_basket(sDelvType, oTarget)
19632{
19633    var oProductName = null;
19634    if (typeof(product_name) !== 'undefined') {
19635        oProductName = {'product_name': product_name};
19636    }
19637    EC$('.xans-product-basketoption').remove();
19638    EC$.get('/product/add_basket.html?delvtype='+sDelvType, oProductName, function(sHtml)
19639        {
19640            sHtml = sHtml.replace(/<script.*?ind-script\/optimizer.php.*?<\/script>/g, '');
19641            // scirpt를 제거하면서 document.ready의 Async 모듈이 실행안되서 강제로 실행함
19642            CAPP_ASYNC_METHODS.init();
19643            create_layer('confirmLayer', sHtml, oTarget);
19644        });
19645}
19646
19647function layer_basket2(sDelvType, oTarget)
19648{
19649    EC$('.xans-order-layerbasket').remove();
19650    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19651    var sUrl = '/product/add_basket2.html?delvtype=' + sDelvType + '&layerbasket=T';
19652
19653    // URL 에 ch_ref 파라미터 추가
19654    if (CAFE24.hasChRef()) {
19655        // 이미 ch_ref가 포함되어 있는지 확인
19656        var urlObj = new URL(sUrl, window.location.origin);
19657        if (!urlObj.searchParams.has('ch_ref')) {
19658            sUrl = CAFE24.attachChRef(sUrl);
19659        }
19660    }
19661
19662    EC$.get(sUrl, '', function(sHtml)
19663    {
19664        sHtml = sHtml.replace(/<script.*?ind-script\/optimizer.php.*?<\/script>/g, '');
19665
19666        // 장바구니 link에 ch_ref 파라미터 추가
19667        var chRefData = null;
19668        if (typeof CAFE24 !== 'undefined' && CAFE24.getChRefData && CAFE24.getChRefData()) {
19669            chRefData = CAFE24.getChRefData();
19670        } else {
19671            chRefData = $(':input[name="ch_ref"]').val();
19672        }
19673
19674        if (chRefData) {
19675            sHtml = sHtml.replace(
19676                /href\s*=\s*["']\/order\/basket\.html(?:\?([^"']*))?["']/g,
19677                function(match, existingQuery) {
19678                    let newUrl = '/order/basket.html';
19679
19680                    if (existingQuery) {
19681                        // 기존 쿼리스트링에서 ch_ref 제거 및 정리
19682                        const cleanQuery = existingQuery
19683                            .replace(/[?&]ch_ref=[^&]*/g, '')
19684                            .replace(/(^&)|(&$)/g, '');
19685
19686                        // 새로운 쿼리스트링 구성
19687                        const queryParams = cleanQuery
19688                            ? `${cleanQuery}&ch_ref=${encodeURIComponent(chRefData)}`
19689                            : `ch_ref=${encodeURIComponent(chRefData)}`;
19690
19691                        newUrl += `?${queryParams}`;
19692                    } else {
19693                        newUrl += `?ch_ref=${encodeURIComponent(chRefData)}`;
19694                    }
19695
19696                    return `href="${newUrl}"`;
19697                }
19698            );
19699        }
19700
19701        //scirpt를 제거하면서 document.ready의 Async 모듈이 실행안되서 강제로 실행함
19702        CAPP_ASYNC_METHODS.init();
19703        create_layer('confirmLayer', sHtml, oTarget);
19704    });
19705}
19706
19707function layer_wishlist(oTarget)
19708{
19709    EC$('.layerWish').remove();
19710    EC$.get('/product/layer_wish.html','' ,function(sHtml)
19711    {
19712        sHtml = sHtml.replace(/<script.*?ind-script\/optimizer.php.*?<\/script>/g, '');
19713        create_layer('confirmLayer', sHtml, oTarget);
19714    });
19715}
19716
19717function go_basket()
19718{
19719    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19720    oTarget.location.href = '/order/basket.html';
19721    if (CAPP_SHOP_FRONT_COMMON_UTIL.isPopupFromThisShopFront() === true) {
19722        self.close();
19723    }
19724}
19725
19726function move_basket_page()
19727{
19728    var sLocation = location;
19729    try {
19730
19731        sLocation = ITEM.setBodyOverFlow(location);
19732    } catch (e) {
19733        PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.move_basket_page');
19734    }
19735
19736    sLocation.href = '/order/basket.html';
19737}
19738
19739/**
19740 * 이미지 확대보기 (상품상세 버튼)
19741 */
19742function go_detail()
19743{
19744    var sUrl = '/product/detail.html?product_no=' + iProductNo;
19745    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19746
19747    if (typeof(iCategoryNo) !== 'undefined') {
19748        sUrl += '&cate_no='+iCategoryNo;
19749    }
19750
19751    if (typeof(iDisplayGroup) !== 'undefined') {
19752        sUrl += '&display_group='+iDisplayGroup;
19753    }
19754
19755    oTarget.location.href = sUrl;
19756    if (CAPP_SHOP_FRONT_COMMON_UTIL.isPopupFromThisShopFront() === true) {
19757        self.close();
19758    }
19759}
19760
19761/**
19762 * 바로구매하기/장바구니담기 Action  - 판매정보 > 구매제한
19763 */
19764function check_action_nologin(sBuyLimitType)
19765{
19766    sMsg = __('CAN.PURCHASE.GROUP', 'GLOBAL.BUY.LIMIT');
19767    if (sBuyLimitType == 'O' || sBuyLimitType == 'D') {
19768        sMsg = __('CAN.PURCHASE.MEMBER.USE', 'SHOP.JS.FRONT.NEW.PRODUCT.ACTION');
19769    }
19770    alert(sMsg);
19771    return false;
19772}
19773
19774/**
19775 * 바로구매하기 Action  - 불량회원 구매제한
19776 */
19777function check_action_block(sMsg)
19778{
19779    if (sMsg == '') {
19780        sMsg = __('쇼핑몰 관리자가 구매 제한을 설정하여 구매하실 수 없습니다.');
19781    }
19782    alert(sMsg);
19783}
19784
19785/**
19786 * 관심상품 등록 - 로그인하지 않았을 경우
19787 */
19788function add_wishlist_nologin(sUrl)
19789{
19790    PRODUCTSUBMIT.setLoginMovePage(sUrl , __('로그인 후 관심상품 등록을 해주세요.'));
19791
19792}
19793
19794/**
19795 * 바로구매하기 / 장바구니 담기 / 관심상품 등록 시 url 이동에 사용하는 메소드
19796 * @param sUrl 이동할 주소
19797 */
19798function btn_action_move_url(sUrl)
19799{
19800    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19801
19802    sLocation = ITEM.setBodyOverFlow(location);
19803
19804    sUrl += '?returnUrl=' + encodeURIComponent(oTarget.location.pathname + oTarget.location.search);
19805    oTarget.location.replace(sUrl);
19806}
19807
19808/**
19809 * return_url 없이 url 이동에 사용하는 메소드
19810 * @param sUrl 이동할 주소
19811 */
19812function btn_action_move_no_return_url(sUrl)
19813{
19814    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
19815    oTarget.location.replace(sUrl);
19816}
19817
19818/**
19819 * 관심상품 등록 - 파라미터 생성
19820 * @param bIsUseOptionSelect 장바구니옵션선택 새모듈 사용여부(basket_option.html, Product_OptionSelectLayer)
19821 */
19822function add_wishlist(sMode, bIsUseOptionSelect)
19823{
19824    var sUrl = '//' + location.hostname;
19825    sUrl += '/exec/front/Product/Wishlist/';
19826    var param = location.search.substring(location.search.indexOf('?') + 1);
19827    sParam = param + '&command=add' + '&from=wish_icon';
19828    sParam += '&referer=' + encodeURIComponent('//' + location.hostname + location.pathname + location.search);
19829
19830    add_wishlist_action(sUrl, sParam, sMode, bIsUseOptionSelect);
19831}
19832
19833var bWishlistSave = false;
19834/**
19835 * @param bIsUseOptionSelect 장바구니옵션선택 새모듈 사용여부(basket_option.html, Product_OptionSelectLayer)
19836 */
19837function add_wishlist_action(sAction, sParam, sMode, bIsUseOptionSelect)
19838{
19839    //연동형 옵션 여부
19840    var bIsOlinkOption = Olnk.isLinkageType(sOptionType);
19841    if (bWishlistSave === true) {
19842        return false;
19843    }
19844    var required_msg = __('품목을 선택해 주세요.');
19845    if (sOptionType !== 'F') {
19846        var aItemCode = ITEM.getWishItemCode();
19847    } else {
19848        var aItemCode = null;
19849    }
19850    var sSelectedItemByEtype = '';
19851
19852    var frm = EC$('#frm_image_zoom');
19853    frm.find(":hidden").remove();
19854    frm.attr('method', 'POST');
19855    frm.attr('action', '/' + sAction);
19856
19857    if (bIsOlinkOption === true) {
19858        if (isNewProductSkin() === false) {
19859            sItemCode = Olnk.getSelectedItemForWishOldSkin(sProductCode, EC$('[id^="product_option_id"]'));
19860
19861            if (sItemCode !== false) {
19862                frm.append(getInputHidden('selected_item_by_etype[]', EC$.toJSON(sItemCode)));
19863                //sSelectedItemByEtype += 'selected_item_by_etype[]='+EC$.toJSON(sItemCode) + '&';
19864                aItemCode.push(sItemCode);
19865            }
19866
19867        } else {
19868            EC$('.soldout_option_box_id,.option_box_id').each(function(i) {
19869                sItemCode = Olnk.getSelectedItemForWish(sProductCode, EC$(this));
19870                if (sItemCode.bCheckNum === false) {
19871                    sItemCode = Olnk.getProductAllSelected(sProductCode , EC$(this) , 1);
19872                }
19873                frm.append(getInputHidden('selected_item_by_etype[]', EC$.toJSON(sItemCode)));
19874                //sSelectedItemByEtype += 'selected_item_by_etype[]='+EC$.toJSON(sItemCode) + '&';
19875                aItemCode.push(sItemCode);
19876            });
19877
19878            // 전부 선택인 경우 필요값 생성한다.
19879            if (sSelectedItemByEtype === '') {
19880                iQuantity = (buy_unit >= product_min ? buy_unit : product_min);
19881                aItemValueNo = Olnk.getProductAllSelected(sProductCode , EC$('[id^="product_option_id"]') , 1);
19882                if (aItemValueNo !== false) {
19883                    frm.append(getInputHidden('selected_item_by_etype[]', EC$.toJSON(aItemValueNo)));
19884                    //sSelectedItemByEtype += 'selected_item_by_etype[]='+EC$.toJSON(aItemValueNo) + '&';
19885                    aItemCode.push(aItemValueNo);
19886                }
19887            }
19888
19889            NEWPRD_ADD_OPTION.setItemAddOptionName(frm);
19890            var iOptionBoxLength = EC$('.option_box_id').length - 1;
19891            EC$('.option_box_id').each(function(i) {
19892
19893                iQuantity = EC$('#' + EC$(this).attr('id').replace('id','quantity')).val();
19894                _aItemValueNo = Olnk.getSelectedItemForBasket(sProductCode, EC$(this), iQuantity);
19895
19896                if (_aItemValueNo.bCheckNum === false) { // 옵션박스는 있지만 값이 선택이 안된경우
19897                    _aItemValueNo = Olnk.getProductAllSelected(sProductCode , EC$(this) , iQuantity);
19898                }
19899
19900                var oItem = EC$('[name="item_code[]"]').eq(i);
19901                var sItemCode = sProductCode + '000A_' + i;
19902
19903                //품목별 추가옵션 셋팅
19904                var ePerAddOption = EC$('.option_products .option').eq(i).find(".input_addoption:visible");
19905                if (ePerAddOption.length > 0) { // 옵션 박스안에서 개별 입력시
19906                    sItemCode = Olnk.getCustomOptionItemCode(sProductCode, iOptionBoxLength, i);
19907                    NEWPRD_ADD_OPTION.setItemPerAddOptionData(sItemCode, ePerAddOption, frm);
19908                } else {
19909                    var sItemAddOption = NEWPRD_ADD_OPTION.getAddOptionValue(oItem.attr('data-item-add-option'));
19910                    NEWPRD_ADD_OPTION.setItemAddOption(sItemCode, sItemAddOption, frm);
19911                }
19912            });
19913
19914
19915        }
19916
19917        if (bIsUseOptionSelect !== true && (/^\*+$/.test(aItemCode) === true || aItemCode == '')) {
19918            alert(required_msg);
19919            return false;
19920        }
19921    } else {
19922        if (isNewProductSkin() === true) {
19923            //품목별 추가옵션 이름 셋팅
19924            NEWPRD_ADD_OPTION.setItemAddOptionName(frm);
19925
19926            EC$('[name="quantity_opt[]"][id^="option_box"]').each(function(i) {
19927
19928                var oItem = EC$('[name="item_code[]"]').eq(i);
19929                var sItemCode = oItem.val();
19930
19931                //품목별 추가옵션 셋팅
19932                var ePerAddOption = EC$('.option_product').eq(i).find(".input_addoption:visible");
19933                if (ePerAddOption.length > 0) { // 옵션 박스안에서 개별 입력시
19934                    NEWPRD_ADD_OPTION.setItemPerAddOptionData(sItemCode, ePerAddOption, frm);
19935                } else {
19936                    var sItemAddOption = NEWPRD_ADD_OPTION.getAddOptionValue(oItem.attr('data-item-add-option'));
19937                    NEWPRD_ADD_OPTION.setItemAddOption(sItemCode, sItemAddOption, frm);
19938                }
19939
19940            });
19941
19942        }
19943    }
19944
19945    if (aItemCode === false && bIsUseOptionSelect !== true) {
19946        if (CAFE24.SHOP_FRONT_PRODUCT_OPTIONLAYER.setLayer(iProductNo, iCategoryNo, 'normal') === true) {
19947            return;
19948        }
19949        alert(required_msg);
19950        return false;
19951    }
19952
19953
19954    if (aItemCode !== null) {
19955        var sItemCode = '';
19956        var aTemp = [];
19957
19958        if (Olnk.isLinkageType(sOptionType) === true) {
19959            frm.append(getInputHidden('selected_item[]', '000A'));
19960            //sParam = sParam + '&' + 'selected_item[]=000A&' + sSelectedItemByEtype;
19961        } else {
19962            for (var x in aItemCode) {
19963                try {
19964                    var opt_id = aItemCode[x].substr(aItemCode[x].length-4, aItemCode[x].length);
19965                    frm.append(getInputHidden('selected_item[]', opt_id));
19966                    //aTemp.push('selected_item[]='+opt_id);
19967                } catch (e) {
19968                    PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.add_wishlist_action');
19969                }
19970            }
19971        }
19972    }
19973
19974    // URLSearchParams 객체를 사용하여 파라미터 파싱
19975    const params = new URLSearchParams(sParam);
19976    if (!params.has('product_no') && (typeof(iProductNo) !== 'undefined' && iProductNo !== '' && iProductNo !== null)) {
19977        frm.append(getInputHidden('product_no', iProductNo));
19978    }
19979    if (!params.has('cate_no') && (typeof(iCategoryNo) !== 'undefined' && iCategoryNo !== '' && iCategoryNo !== null)) {
19980        frm.append(getInputHidden('cate_no', iCategoryNo));
19981    }
19982    frm.append(getInputHidden('option_type', sOptionType));
19983    //sParam = sParam + '&product_no='+iProductNo;
19984
19985
19986    // 추가 옵션 체크 (품목기반 추가옵션일때는 폼제출때 검증 불필요)
19987    //뉴모듈사용시에는 체크안함
19988    if (bIsUseOptionSelect !== true && (NEWPRD_ADD_OPTION.isItemBasedAddOptionType() !== true && checkAddOption() === false)) {
19989        return false;
19990    }
19991
19992    // 추가옵션
19993    var aAddOptionStr = new Array();
19994    var aAddOptionRow = new Array();
19995    if (add_option_name) {
19996        for (var i=0; i<add_option_name.length; i++) {
19997            if (add_option_name[i] !== '' && EC$('#' + add_option_id + i).length > 0) {
19998                aAddOptionRow.push(add_option_name[i] + '*' + EC$('#' + add_option_id + i).val());
19999            }
20000        }
20001    }
20002    aAddOptionStr.push(aAddOptionRow);
20003
20004    // 단일 상품의 개별 추가 입력 옵션 처리
20005    if (has_option === 'F' && typeof add_option_input !== 'undefined') {
20006        NEWPRD_ADD_OPTION.addItem(ITEM.getItemCode()[0]);
20007        var iAddOptionIndex = NEWPRD_ADD_OPTION.getLastIndex();
20008        for (var x in add_option_input) {
20009            if (add_option_input.hasOwnProperty(x) === true) {
20010                NEWPRD_ADD_OPTION.addCustomOption(iAddOptionIndex, {
20011                    type: 'text',
20012                    value: EC$('#' + add_option_input[x].id).val(),
20013                    info: add_option_input[x].info
20014                }, 'input');
20015            }
20016        }
20017    }
20018
20019    frm.append(getInputHidden('add_option', aAddOptionStr.join('|')));
20020    //sParam += '&add_option=' + encodeURIComponent(aAddOptionStr.join('|'));
20021
20022    // 파일첨부 옵션 유효성 체크
20023    if (bIsUseOptionSelect !== true && FileOptionManager.checkValidation() === false) return;
20024
20025    bWishlistSave = true;
20026
20027    // 파일첨부 옵션의 파일업로드가 없을 경우 바로 관심상품 넣기
20028    if (FileOptionManager.existsFileUpload() === false) {
20029        NEWPRD_ADD_OPTION.setItemPerAddOptionForm(frm);
20030        sParam = sParam + '&' + frm.serialize();
20031        add_wishlist_request(sParam, sMode);
20032    // 파일첨부 옵션의 파일업로드가 있으면
20033    } else {
20034        FileOptionManager.upload(function(mResult) {
20035            // 파일업로드 실패
20036            if (mResult===false) {
20037                bWishlistSave = false;
20038                return false;
20039            }
20040
20041            // 파일업로드 성공
20042            for (var sId in mResult) {
20043                // 해당 품목에 파일 첨부 옵션 항목 추가
20044                NEWPRD_ADD_OPTION.pushFileList(sId, mResult);
20045                frm.append(getInputHidden(sId, FileOptionManager.encode(mResult[sId])));
20046                //sParam += '&'+sId+'='+FileOptionManager.encode(mResult[sId]);
20047            }
20048
20049
20050            NEWPRD_ADD_OPTION.setItemPerAddOptionForm(frm);
20051
20052            sParam = sParam + '&' + frm.serialize();
20053            add_wishlist_request(sParam, sMode);
20054        });
20055    }
20056}
20057
20058function add_wishlist_request(sParam, sMode)
20059{
20060    var sUrl = '/exec/front/Product/Wishlist/';
20061
20062    EC$.post(
20063        sUrl,
20064        sParam,
20065        function(data) {
20066            if (sMode != 'back') {
20067                add_wishlist_result(data);
20068            }
20069            bWishlistSave = false;
20070        },
20071        'json');
20072}
20073
20074function add_wishlist_result(aData, aPrdData)
20075{
20076    var oTarget = CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame();
20077    var agent = navigator.userAgent.toLowerCase();
20078
20079    if (aData == null) return;
20080    //새로운 모듈 사용시에는 중복되어있어도 처리된것으로 간주함.. 왜 그렇게하는지는 이해불가
20081    if (aData.result == 'SUCCESS' || (aData.bIsUseOptionSelect === true && aData.result === 'NO_TARGET')) {
20082
20083        bBuyLayer = ITEM.setBodyOverFlow(true);
20084
20085        if (typeof iProductNo !== 'undefined') {
20086            var iSendProductNo = iProductNo;
20087        } else if (typeof aPrdData !== 'undefined') {
20088            var iSendProductNo = aPrdData.product_no;
20089        }
20090
20091        if (iSendProductNo) {
20092            CAFE24.PLUSAPP_BRIDGE.addWishList(iSendProductNo);
20093            if (typeof CAFE24.KAKAO_PIXEL_BRIDGE !== 'undefined') {
20094                CAFE24.KAKAO_PIXEL_BRIDGE.addWishList(iSendProductNo);
20095            }
20096        }
20097
20098        if (CAPP_ASYNC_METHODS.hasOwnProperty('WishList') === true && typeof iProductNo !== 'undefined') {
20099            // 관심상품 추가시 sessionStorage 추가
20100            CAPP_ASYNC_METHODS.WishList.setSessionStorageItem(iProductNo, aData.command);
20101        }
20102
20103        if (CAPP_ASYNC_METHODS.hasOwnProperty('Wishcount') === true) {
20104            CAPP_ASYNC_METHODS.Wishcount.restoreCache();
20105            CAPP_ASYNC_METHODS.Wishcount.execute();
20106        }
20107
20108        if (aData.confirm == 'T') {
20109            layer_wishlist(CAPP_SHOP_FRONT_COMMON_UTIL.findTargetFrame());
20110            return;
20111        }
20112
20113        if ($('.ec-product-listwishicon').attr('icon_status') === 'off') {
20114            CAPP_PRODUCT_LIST_WISHICON.getResultWishIconAjax(aData, $('.ec-product-listwishicon'));
20115        }
20116        alert(__('관심상품으로 등록되었습니다.'));
20117    } else if (aData.result == 'ERROR') {
20118        alert(__('실패하였습니다.'));
20119    } else if (aData.result == 'NOT_LOGIN') {
20120        alert(__('회원 로그인 후 이용하실 수 있습니다.'));
20121    } else if (aData.result == 'INVALID_REQUEST') {
20122        alert(__('파라미터가 잘못되었습니다.'));
20123    } else if (aData.result == 'NO_TARGET') {
20124        alert(__('이미 등록되어 있습니다.'));
20125    } else if (aData.result == 'INVALID_PRODUCT') {
20126        alert(__('파라미터가 잘못되었습니다.'));
20127    }
20128}
20129
20130/**
20131* 추가된 함수
20132* 해당 value값을 받아 replace 처리
20133* @param string sValue value
20134* @return string replace된 sValue
20135*/
20136function replaceCheck(sName,sValue)
20137{
20138   //ECHOSTING-9736
20139   if (typeof(sValue) === "string" && (sName == "option_add[]" || sName.indexOf("item_option_add") === 0)) {
20140        sValue = sValue.replace(/'/g, '\\&#039;');
20141   }
20142   // 타입이 string 일때 연산시 단일 따움표 " ' " 문자를 " ` " 액센트 문자로 치환하여 깨짐을 방지
20143   return sValue;
20144}
20145
20146
20147/**
20148 * name, value값을 받아 input hidden 태그 반환
20149 *
20150 * @param string sName name
20151 * @param string sValue value
20152 * @return string input hidden 태그
20153 */
20154function getInputHidden(sName, sValue)
20155{
20156    sValue = replaceCheck(sName,sValue); // 추가된 부분 (replaceCheck 함수 호출)
20157    return EC$('<input>').attr({'type': 'hidden', 'name': sName}).val(sValue);
20158}
20159
20160
20161/**
20162 * 필수옵션이 선택되었는지 체크
20163 *
20164 * @return bool 필수옵션이 선택되었다면 true, 아니면 false 반환
20165 */
20166function checkOptionRequired(sReq)
20167{
20168    var bResult = true;
20169    // 옵션이 없다면 필수값 체크는 필요없음.
20170    if (has_option === 'F') {
20171        return bResult;
20172    }
20173    var sTargetOptionId = product_option_id;
20174    if (sReq != null) {
20175        sTargetOptionId = sReq;
20176    }
20177
20178    if (option_type === 'F') {
20179        // 단독구성
20180        var iOptionCount = EC$('select[id^="' + sTargetOptionId + '"][required="true"]').length;
20181        if (iOptionCount > 0) {
20182            if (ITEM.getItemCode() === false) {
20183                bResult = false;
20184                return false;
20185            }
20186
20187            var aRequiredOption = new Object();
20188            var aItemCodeList = ITEM.getItemCode();
20189            // 필수 옵션정보와 선택한 옵션 정보 비교
20190            for (var i=0; i<aItemCodeList.length; i++) {
20191                var sTargetItemCode = aItemCodeList[i];
20192                EC$('select[id^="' + sTargetOptionId + '"][required="true"] option').each(function() {
20193                    if (EC$(this).val() == sTargetItemCode) {
20194                        var sProductOptionId = EC$(this).parent().attr('id');
20195                        aRequiredOption[sProductOptionId] = true;
20196                    }
20197                });
20198
20199            }
20200            // 필수옵션별 개수보다 선택한 옵션개수가 적을경우 리턴
20201            if (iOptionCount > Object.size(aRequiredOption)) {
20202                bResult = false;
20203                return bResult;
20204            }
20205        }
20206    } else {
20207        if (Olnk.isLinkageType(sOptionType) === true) {
20208            if (isNewProductSkin() === false) {
20209                EC$('select[id^="' + product_option_id + '"][required="true"]').each(function() {
20210                    var sel = parseInt(EC$(this).val());
20211
20212                    if (isNaN(sel) === true) {
20213                        EC$(this).focus();
20214                        bResult = false;
20215                        return false;
20216                    }
20217                });
20218                // 추가 구매 check
20219                EC$('.' + EC$.data(document, 'multiple_option_select_class')).each(function(i)
20220                {
20221                    if (EC$(this).prop('required') === true) {
20222                        var sel = parseInt(EC$(this).val());
20223
20224                        if (isNaN(sel) === true) {
20225                            EC$(this).focus();
20226                            bResult = false;
20227                            return false;
20228                        }
20229                    }
20230                });
20231            } else { // 연동형 사용중이면서 뉴스킨
20232                var aItemCodeList = ITEM.getItemCode();
20233                if (aItemCodeList === false) {
20234                    bResult = false;
20235                    return false;
20236                }
20237                // 연동형 옵션의 버튼 사용중이지만 선택된 품목이 없는 경우 , 뉴스킨에서만 동작해야 함.
20238                if (Olnk.getOptionPushbutton(EC$('#option_push_button')) === true && EC$('.option_box_id').length === 0) {
20239                    bResult = false;
20240                    return false;
20241                }
20242            }
20243            return bResult;
20244        }
20245        if (ITEM.getItemCode() === false) {
20246            bResult = false;
20247            return false;
20248        }
20249        // 조합구성
20250        if (item_listing_type == 'S') {
20251            // 분리선택형
20252            var eTarget = CAFE24.UTIL.parseJSON(option_value_mapper);
20253            for (var x in eTarget) {
20254                if (ITEM.getItemCode().indexOf(eTarget[x]) > -1) {
20255                    bResult = true;
20256                    break;
20257                } else {
20258                    bResult = false;
20259                }
20260            }
20261            if (bResult === false) {
20262                bResult = false;
20263                return false;
20264            }
20265        } else {
20266            EC$('select[id^="' + product_option_id + '"][required="true"]').each(function() {
20267                var eTarget = EC$(this).find('option[value!="*"][value!="**"]');
20268                bResult = false;
20269                eTarget.each(function() {
20270                    if (ITEM.getItemCode().indexOf(EC$(this).val()) > -1) {
20271                        bResult = true;
20272                        return false;
20273                    }
20274                });
20275                if (bResult === false) {
20276                    return false;
20277                }
20278            });
20279        }
20280    }
20281
20282    return bResult;
20283}
20284
20285/**
20286 * 추가 옵션 입력값 체크
20287 *
20288 * @return bool 모든 추가옵션에 값이 입력되었다면 true, 아니면 false
20289 *
20290 */
20291/**
20292 * 추가 입력 옵션의 값 체크
20293 * @param string sReq 셀렉터를 기본값 이외로 사용할 경우
20294 * @param object oParent 전체 인풋이 아닌 특정 객체 하위의 엘리먼트만 검사할경우
20295 * @returns {boolean}
20296 */
20297function checkAddOption(sReq, oParent)
20298{
20299    var sAddOptionField = add_option_id;
20300
20301    var sAddOptionSelector = '[id^="' + sAddOptionField + '"]:not(.input_peraddoption)';
20302    if (sReq != null) {
20303        sAddOptionField = sReq;
20304        sAddOptionSelector = '[id="' + sAddOptionField + '"]:not(.input_peraddoption)';
20305    }
20306    var oTargetElement = EC$(sAddOptionSelector);
20307    if (oParent !== null && typeof(oParent) !== 'undefined') {
20308        oTargetElement = oParent.find(sAddOptionSelector);
20309    }
20310
20311    return NEWPRD_ADD_OPTION.validateAddOptionForm(oTargetElement);
20312}
20313
20314/**
20315 * 수량 가져오기
20316 *
20317 * @return mixed 정상적인 수량이면 수량(integer) 반환, 아니면 false 반환
20318 */
20319function getQuantity()
20320{
20321    // 뉴상품인데 디자인이 수정안됐을 수 있다.
20322    if (isNewProductSkin() === false) {
20323        iQuantity = parseInt(EC$(quantity_id).val(),10);
20324    } else {
20325        if (has_option == 'T') {
20326            var iQuantity = 0;
20327
20328            if (Olnk.isLinkageType(sOptionType) === true) {
20329                iQuantity = parseInt(EC$(quantity_id).val(),10);
20330                return iQuantity;
20331            }
20332
20333            EC$('[name="quantity_opt[]"]').each(function() {
20334                iQuantity = iQuantity + parseInt(EC$(this).val(),10);
20335            });
20336        } else {
20337            var iQuantity = parseInt(EC$(quantity_id).val().replace(/^[\s]+|[\s]+$/g,'').match(/[\d\-]+/),10);
20338            if (isNaN(iQuantity) === true || EC$(quantity_id).val() == '' || EC$(quantity_id).val().indexOf('.') > 0) {
20339                return false;
20340            }
20341        }
20342
20343    }
20344
20345    return iQuantity;
20346}
20347
20348/**
20349 * 수량 체크
20350 *
20351 * @return mixed 올바른 수량이면 수량을, 아니면 false
20352 */
20353function checkQuantity()
20354{
20355    // 수량 가져오기
20356    var iQuantity = getQuantity();
20357
20358    if (isNewProductSkin() === false) {
20359        if (iQuantity === false) return false;
20360
20361        // 구스킨의 옵션 추가인 경우 수량을 모두 합쳐야 함..하는수 없이 each추가
20362        // 재고 관련도 여기서 하나?
20363        if (Olnk.isLinkageType(option_type) === true) {
20364            var sOptionIdTmp = '';
20365            EC$('select[id^="' + product_option_id + '"]').each(function() {
20366                if (/^\*+$/.test(EC$(this).val()) === false) {
20367                    sOptionIdTmp = EC$(this).val();
20368                    return false;
20369                }
20370            });
20371
20372            EC$('.EC_MultipleOption').each(function(i) {
20373                iQuantity += parseInt(EC$(this).find('.' + EC$.data(document,'multiple_option_quantity_class')).val(),10);
20374            });
20375
20376            if (Olnk.getStockValidate(sOptionIdTmp , iQuantity) === true) {
20377                alert(__('상품의 수량이 재고수량 보다 많습니다.'));
20378                EC$(quantity_id).focus();
20379                return false;
20380            }
20381        }
20382
20383        if (iQuantity < product_min) {
20384            alert(sprintf(__('최소 주문수량은 %s개 입니다.'), product_min));
20385            EC$(quantity_id).focus();
20386            return false;
20387        }
20388        if (iQuantity > product_max && product_max > 0) {
20389            alert(sprintf(__('최대 주문수량은 %s개 입니다.'), product_max));
20390            EC$(quantity_id).focus();
20391            return false;
20392        }
20393
20394    } else {
20395        var bResult = true;
20396        var bSaleMainProduct = false;
20397        var aQuantity = new Array();
20398        var iTotalOuantity = 0;
20399        var iProductMin = product_min;
20400        var iProductMax = product_max;
20401
20402        var sTotalProductSelector = '#totalProducts > table > tbody';
20403        if (CAFE24.checkChannelUI() === true) {
20404            sTotalProductSelector = '#totalProducts > div';
20405        }
20406        EC$(sTotalProductSelector).not('.add_products').find('[name="quantity_opt[]"]').each(function() {
20407            // 본상품 구매여부
20408            bSaleMainProduct = true;
20409            iQuantity = parseInt(EC$(this).val());
20410
20411            var iProductNum = iProductNo;
20412            // 추가 구성상품인 경우 product_min ,  product_max 값은 다른값을 비교해야 함..
20413            if (EC$(this).attr('id').indexOf('add_') > -1) {
20414                iProductMin = EC$('#'+EC$(this).attr('id').replace('quantity','productmin')).val();
20415                iProductMax = EC$('#'+EC$(this).attr('id').replace('quantity','productmax')).val();
20416                var iProductNum = EC$('#'+EC$(this).attr('id').replace('quantity','id')).attr('class').replace('option_add_box_','');
20417            }
20418            if (typeof(aQuantity[iProductNum]) === 'undefined') {
20419                aQuantity[iProductNum] = new Array();
20420            }
20421            aQuantity[iProductNum].push(iQuantity);
20422
20423            // 상품기준의 경우 품목 총합으로 판단
20424            if (order_limit_type !== 'P') {
20425                if (iQuantity < iProductMin) {
20426                    alert(sprintf(__('상품별 최소 주문수량은 %s 입니다.'), iProductMin));
20427                    EC$(quantity_id).focus();
20428                    bResult = false;
20429                    return false;
20430                }
20431                if (iQuantity > iProductMax && iProductMax > 0) {
20432                    alert(sprintf(__('상품별 최대 주문수량은 %s 입니다.'), iProductMax));
20433                    EC$(quantity_id).focus();
20434                    bResult = false;
20435                    return false;
20436                }
20437            }
20438            iTotalOuantity = iTotalOuantity + iQuantity;
20439        });
20440
20441        if (bResult == false) {
20442            return bResult;
20443        }
20444        if (typeof CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE === 'object') {
20445            for (var iProductNum in aQuantity) {
20446                if (aQuantity.hasOwnProperty(iProductNum) === false) {
20447                    continue;
20448                }
20449                if (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.oBundleConfig.hasOwnProperty(iProductNum) === false) {
20450                    continue;
20451                }
20452
20453                if (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.isValidQuantity(aQuantity[iProductNum], iProductNum) === false) {
20454                    return false;
20455                }
20456            }
20457        }
20458        // 본상품 없이 구매가능하기때문에 본상품있을떄만 체크
20459        if (bSaleMainProduct === true) {
20460            if (order_limit_type === 'P') {
20461                if (iTotalOuantity < iProductMin) {
20462                    alert(sprintf(__('최소 주문수량은 %s개 입니다.'), iProductMin));
20463                    bResult = false;
20464                    return false;
20465                }
20466                if (iTotalOuantity > iProductMax && iProductMax > 0) {
20467                    alert(sprintf(__('최대 주문수량은 %s개 입니다.'), iProductMax));
20468                    bResult = false;
20469                    return false;
20470                }
20471            }
20472            if (buy_unit_type === 'P') {
20473                if (iTotalOuantity % parseInt(buy_unit, 10) !== 0) {
20474                    alert(sprintf(__('구매 주문단위는 %s개 입니다.'), parseInt(buy_unit, 10)));
20475                    bResult = false;
20476                    return false;
20477                }
20478            }
20479        }
20480        if (EC$('.add_products').find('[name="quantity_opt[]"]').length > 0) {
20481            var aTotalQuantity = {};
20482            EC$('.add_products').find('[name="quantity_opt[]"]').each(function () {
20483                    iQuantity = parseInt(EC$(this).val());
20484                    if (typeof(aTotalQuantity[EC$(this).attr('product-no')]) === 'undefined' || aTotalQuantity[EC$(this).attr('product-no')] < 1) {
20485                        aTotalQuantity[EC$(this).attr('product-no')] = 0;
20486                    }
20487                    aTotalQuantity[EC$(this).attr('product-no')] += parseInt(EC$(this).val(), 10);
20488
20489                }
20490            );
20491
20492            for (var iProductNo in aTotalQuantity) {
20493                var aProductQuantityInfo = ProductAdd.getProductQuantityInfo(iProductNo);
20494
20495                if (aProductQuantityInfo.order_limit_type === 'P') {
20496                    if (aTotalQuantity[iProductNo] < aProductQuantityInfo.product_min) {
20497                        alert(sprintf(__('최소 주문수량은 %s개 입니다.'), aProductQuantityInfo.product_min));
20498                        bResult = false;
20499                        return false;
20500                    }
20501                    if (aTotalQuantity[iProductNo] > aProductQuantityInfo.product_max && aProductQuantityInfo.product_max > 0) {
20502                        alert(sprintf(__('최대 주문수량은 %s개 입니다.'), aProductQuantityInfo.product_max));
20503                        bResult = false;
20504                        return false;
20505                    }
20506                }
20507                if (aProductQuantityInfo.buy_unit_type === 'P') {
20508                    if (aTotalQuantity[iProductNo] % parseInt(aProductQuantityInfo.buy_unit, 10) !== 0) {
20509                        alert(sprintf(__('구매주문단위는 %s개 입니다.'), parseInt(aProductQuantityInfo.buy_unit, 10)));
20510                        bResult = false;
20511                        return false;
20512                    }
20513                }
20514            }
20515        }
20516        if (bResult == false) {
20517            return bResult;
20518        }
20519    }
20520
20521    return iQuantity;
20522}
20523
20524function commify(n)
20525{
20526    var reg = /(^[+-]?\d+)(\d{3})/; // 정규식
20527    n += ''; // 숫자를 문자열로 변환
20528    while (reg.test(n)) {
20529        n = n.replace(reg, '$1' + ',' + '$2');
20530    }
20531    return n;
20532}
20533
20534var isClose = 'T';
20535function optionPreview(obj, sAction, sProductNo, closeType)
20536{
20537    var sPreviewId = 'btn_preview_';
20538    var sUrl = '/product/option_preview.html';
20539    var layerId = EC$('#opt_preview_' + sAction + '_' + sProductNo);
20540
20541    // layerId = action명 + product_no 로 이루어짐 (한 페이지에 다른 종류의 상품리스트가 노출될때 구분 필요)
20542    if (EC$(layerId).length > 0) {
20543        EC$(layerId).show();
20544    } else if (sProductNo != '') {
20545        EC$.post(sUrl, 'product_no=' + sProductNo + '&action=' + sAction, function(result)
20546        {
20547            EC$(obj).after(result.replace(/[<]script( [^ ]+)? src=\"[^>]*>([\s\S]*?)[<]\/script>/g,""));
20548        });
20549    }
20550}
20551
20552function closeOptionPreview(sAction, sProductNo)
20553{
20554    isClose = 'T';
20555    setTimeout("checkOptionPreview('" + sAction + "','" + sProductNo + "')", 150);
20556}
20557
20558function checkOptionPreview(sAction, sProductNo)
20559{
20560    var layerId = EC$('#opt_preview_' + sAction + '_' + sProductNo);
20561    if (isClose == 'T') EC$(layerId).hide();
20562}
20563
20564function openOptionPreview(sAction, sProductNo)
20565{
20566    isClose = 'F';
20567    var layerId = EC$('#opt_preview_' + sAction + '_' + sProductNo);
20568    EC$(layerId).show();
20569
20570    EC$(layerId).mousemouseenter(function()
20571    {
20572        EC$(layerId).show();
20573    }).mouseleave(function()
20574    {
20575        EC$(layerId).hide();
20576    });
20577
20578}
20579
20580/**
20581 * 네이버 페이 주문하기
20582 */
20583function nv_add_basket_1_product()
20584{
20585    bIsMobile = false;
20586
20587    if (_isProc == 'F') {
20588        alert(__("네이버 페이 입점상태를 확인하십시오."));
20589        return;
20590    }
20591
20592    if (typeof(set_option_data) !== 'undefined') {
20593        alert(__('세트상품은 네이버 페이 구매가 불가하오니, 쇼핑몰 바로구매를 이용해주세요. 감사합니다.'));
20594        return;
20595    }
20596
20597    product_submit('naver_checkout', '/exec/front/order/basket/');
20598}
20599
20600/**
20601 * 네이버 페이 찜하기
20602 */
20603function nv_add_basket_2_product()
20604{
20605    if (_isProc == 'F') {
20606        alert(__("네이버 페이 입점상태를 확인하십시오."));
20607        return;
20608    }
20609
20610    window.open("/exec/front/order/navercheckoutwish?product_no=" + iProductNo, "navercheckout_basket",
20611            'scrollbars=yes,status=no,toolbar=no,width=450,height=300');
20612}
20613
20614/**
20615 * 네이버 페이 주문하기
20616 */
20617function nv_add_basket_1_m_product()
20618{
20619    bIsMobile = true;
20620
20621    if (_isProc == 'F') {
20622        alert(__("네이버 페이 입점상태를 확인하십시오."));
20623        return;
20624    }
20625
20626    if (typeof(set_option_data) !== 'undefined') {
20627        alert(__('세트상품은 네이버 페이 구매가 불가하오니, 쇼핑몰 바로구매를 이용해주세요. 감사합니다.'));
20628        return;
20629    }
20630
20631    product_submit('naver_checkout', '/exec/front/order/basket/');
20632}
20633
20634/**
20635 * 네이버 페이 찜하기
20636 */
20637function nv_add_basket_2_m_product()
20638{
20639    if (_isProc == 'F') {
20640        alert(__("네이버 페이 입점상태를 확인하십시오."));
20641        return;
20642    }
20643
20644    window.location.href = "/exec/front/order/navercheckoutwish?product_no=" + iProductNo;
20645    //window.open("/exec/front/order/navercheckoutwish?product_no=" + iProductNo, "navercheckout_basket", 'scrollbars=yes,status=no,toolbar=no,width=450,height=300');
20646}
20647
20648/**
20649 * 옵션 추가 구매시에 같은 옵션을 검사하는 함수
20650 *
20651 * @returns Boolean
20652 */
20653function duplicateOptionCheck()
20654{
20655    var bOptionDuplicate = getOptionDuplicate();
20656    //var bAddOptionDuplicate = getAddOptionDuplicate();
20657
20658    if (bOptionDuplicate !== true) { //}&& bAddOptionDuplicate !== true) {
20659        alert(__('동일한 옵션의 상품이 있습니다.'));
20660        return false;
20661    }
20662
20663    return true;
20664}
20665
20666/**
20667 * 텍스트 인풋 옵션 중복 체크
20668 *
20669 * @returns {Boolean}
20670 */
20671function getAddOptionDuplicate()
20672{
20673    var aOptionRow = new Array();
20674    var iOptionLength = 0;
20675    var aOptionValue = new Array();
20676    var bReturn = true;
20677    // 기본 옵션
20678    EC$('[id^="' + add_option_id + '"]').each(function()
20679    {
20680        aOptionRow.push(EC$(this).val());
20681    });
20682    aOptionValue.push(aOptionRow.join(',@,'));
20683    EC$('.EC_MultipleOption').each(function()
20684    {
20685        aOptionRow = new Array();
20686        EC$(EC$(this).find('.' + EC$.data(document, 'multiple_option_input_class'))).each(function()
20687        {
20688            aOptionRow.push(EC$(this).val());
20689        });
20690        var sOptionRow = aOptionRow.join(',@,');
20691        if (EC$.inArray(sOptionRow, aOptionValue) > -1) {
20692            bReturn = false;
20693            return false;
20694        } else {
20695            aOptionValue.push(sOptionRow);
20696        }
20697    });
20698    return bReturn;
20699}
20700/**
20701 * 일반 셀렉트박스형 옵션 체크 함수
20702 *
20703 * @returns {Boolean}
20704 */
20705function getOptionDuplicate() {
20706    // 선택여부는 이미 선택이 되어 있음
20707    var aOptionId = new Array();
20708    var aOptionValue = new Array();
20709    var aOptionRow = new Array();
20710    var iOptionLength = 0;
20711    // 기본 옵션
20712    EC$('select[id^="' + product_option_id + '"]').each(function (i) {
20713        aOptionValue.push(EC$(this).val());
20714        iOptionLength++;
20715    });
20716    // 추가 구매
20717    EC$('.' + EC$.data(document, 'multiple_option_select_class')).each(function (i) {
20718        aOptionValue.push(EC$(this).val());
20719    });
20720
20721    var aOptionRow = new Array();
20722    for (var x in aOptionValue) {
20723        var sOptionValue = aOptionValue[x];
20724        aOptionRow.push(sOptionValue);
20725        if (x % iOptionLength == iOptionLength - 1) {
20726            var sOptionId = aOptionRow.join('-');
20727
20728            if (EC$.inArray(sOptionId, aOptionId) > -1) {
20729                return false;
20730            }
20731            aOptionId.push(sOptionId);
20732            aOptionRow = new Array();
20733        }
20734    }
20735
20736    return true;
20737}
20738
20739//sms 재입고
20740function action_sms_restock(sParam)
20741{
20742    // 모바일 접속 및 레이어 팝업 여부 확인
20743    if (typeof(EC_SHOP_FRONT_PRODUCT_SMS_RESTOCK_LAYER) !== 'undefined') {
20744        if (EC_SHOP_FRONT_PRODUCT_SMS_RESTOCK_LAYER.createSmsRestockLayerDisplayResult(sParam) === true) {
20745            return;
20746        }
20747    }
20748
20749    window.open('#none', 'sms_restock' ,'width=459, height=490, scrollbars=yes');
20750    EC$('#frm_image_zoom').attr('target', 'sms_restock');
20751    EC$('#frm_image_zoom').attr('action', '/product/sms_restock.html');
20752    EC$('#frm_image_zoom').submit();
20753}
20754
20755//email 재입고
20756function action_email_restock(iProductNo)
20757{
20758    if (typeof(iProductNo) === 'undefined') {
20759        iProductNo = '';
20760    }
20761    if ((window.navigator.standalone || (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches)) === true) {
20762        window.open('/product/email_restock.html?' + EC$('#frm_image_zoom').serialize(), 'email_restock' ,'width=459, height=490, scrollbars=yes');
20763    } else {
20764        window.open('#none', 'email_restock' ,'width=459, height=490, scrollbars=yes');
20765        EC$('#frm_image_zoom').attr('target', 'email_restock');
20766        EC$('#frm_image_zoom').attr('action', '/product/email_restock.html?product_no' + iProductNo);
20767        EC$('#frm_image_zoom').submit();
20768    }
20769}
20770
20771// 최대 할인쿠폰 다운받기 팝업
20772function popupDcCoupon(product_no, coupon_no, cate_no, opener_url, location)
20773{
20774    var aUrl = [];
20775    if (location === 'Front' || typeof location === 'undefined') {
20776        aUrl.push('product');
20777    }
20778    aUrl.push('coupon_popup.html');
20779    window.open(EC_ROUTE.getPrefixUrl('/' + aUrl.join('/')) + "?product_no=" + product_no + "&coupon_no=" + coupon_no + "&cate_no=" + cate_no + "&opener_url=" + opener_url, "popupDcCoupon", "toolbar=no,scrollbars=no,resizable=yes,width=800,height=640,left=0,top=0");
20780}
20781
20782/**
20783 * 관련상품 열고 닫기
20784 */
20785function ShowAndHideRelation()
20786{
20787    try {
20788        var sRelation = EC$('ul.mSetPrd').parent();
20789        var sRelationDisp = sRelation.css('display');
20790        if (sRelationDisp === 'none') {
20791            EC$('#setTitle').removeClass('show');
20792            sRelation.show();
20793        } else {
20794            EC$('#setTitle').addClass('show');
20795            sRelation.hide();
20796        }
20797    } catch (e) { }
20798}
20799
20800var ITEM = {
20801    getItemCode: function()
20802    {
20803        var chk_has_opt = '';
20804        try {
20805            chk_has_opt = has_option;
20806        } catch (e) {chk_has_opt = 'T';}
20807
20808        if (chk_has_opt == 'F') {
20809            return [item_code];
20810        } else {
20811            // 필수값 체크
20812            var bRequire = false;
20813            // 옵션이 없음
20814            if (EC$('[id^="product_option_id"]').length < 1) {
20815                return false;
20816            }
20817            EC$('[id^="product_option_id"]').each(function() {
20818                if (EC$(this).prop('required') === true || EC$(this).attr('required') === 'required') {
20819                    bRequire = true;
20820                    return false;
20821                }
20822            });
20823
20824            var aItemCode = [];
20825            if (bRequire === true) {
20826                if (EC$('#totalProducts').length === 0 || (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === false)) {
20827                    sItemCode = this.getOldProductItemCode();
20828                    if (sItemCode !== false) {
20829                        if (typeof(sItemCode) === 'string') {
20830                            aItemCode.push(sItemCode);
20831                        } else {
20832                            aItemCode = sItemCode;
20833                        }
20834                    } else {
20835                        // 옵션이 선택되지 않음
20836                        return false;
20837                    }
20838                } else {
20839                    if (EC$('.option_box_id').length == 0) {
20840                        // 옵션이 선택되지 않음
20841                        return false;
20842                    }
20843                    EC$('.option_box_id').each(function() {
20844                        aItemCode.push(EC$(this).val());
20845                    });
20846                }
20847            }
20848
20849
20850            return aItemCode;
20851        }
20852    },
20853    getWishItemCode: function()
20854    {
20855        var chk_has_opt = '';
20856        try {
20857            chk_has_opt = has_option;
20858        } catch (e) {chk_has_opt = 'T';}
20859
20860        if (chk_has_opt == 'F') {
20861            return [item_code];
20862        } else {
20863            // 필수값 체크
20864            var bRequire = false;
20865            EC$('[id^="product_option_id"]').each(function() {
20866                if (EC$(this).prop('required') === true || EC$(this).attr('required') === 'required') {
20867                    bRequire = true;
20868                    return false;
20869                }
20870            });
20871
20872            var aItemCode = new Array();
20873            if (bRequire === true) {
20874                if (EC$('#totalProducts').length === 0) {
20875                    sItemCode = this.getOldProductItemCode();
20876                    if (sItemCode !== false) {
20877                        if (typeof(sItemCode) === 'string') {
20878                            aItemCode.push(sItemCode);
20879                        } else {
20880                            aItemCode = sItemCode;
20881                        }
20882                    } else {
20883                        // 옵션이 선택되지 않음
20884                        return false;
20885                    }
20886                } else {
20887                    if (EC$('.soldout_option_box_id,.option_box_id').length == 0) {
20888                        // 옵션이 선택되지 않음
20889                        return false;
20890                    }
20891                    EC$('.soldout_option_box_id,.option_box_id').each(function() {
20892                        aItemCode.push(EC$(this).val());
20893                    });
20894                }
20895            }
20896
20897            return aItemCode;
20898        }
20899    },
20900    getOldProductItemCode: function(sSelector)
20901    {
20902        if (sSelector === undefined) {
20903            sSelector = '[id^="product_option_id"]';
20904        }
20905        var sItemCode = null;
20906        // 뉴상품 옵션 선택 구매
20907        if (has_option === 'F') {
20908            // 화면에 있음
20909            sItemCode = item_code;
20910        } else {
20911            if (item_listing_type == 'S') {
20912                var aOptionValue = new Array();
20913                EC$(sSelector).each(function() {
20914                    if (ITEM.isOptionSelected(EC$(this).val()) === true) {
20915                        aOptionValue.push(EC$(this).val());
20916                    }
20917                });
20918
20919                if (option_type === 'T') {
20920                    var aCodeMap = CAFE24.UTIL.parseJSON(option_value_mapper);
20921                    sItemCode = aCodeMap[aOptionValue.join('#$%')];
20922                } else {
20923                    sItemCode = aOptionValue;
20924                }
20925            } else {
20926                sItemCode = EC$(sSelector).val();
20927            }
20928        }
20929
20930        if (sItemCode === undefined) {
20931            return false;
20932        }
20933
20934        return sItemCode;
20935    },
20936    isOptionSelected: function(aOption)
20937    {
20938        var sOptionValue = null;
20939        if (typeof aOption === 'string') {
20940            sOptionValue = aOption;
20941        } else {
20942            if (aOption.length === 0) return false;
20943            sOptionValue = aOption.join('-|');
20944        }
20945
20946        sOptionValue = '-|'+sOptionValue+'-|';
20947        return !(/-\|\*{1,2}-\|/g).test(sOptionValue);
20948    },
20949    setBodyOverFlow: function(sType)
20950    {
20951        var sLocation = location;
20952        var bBuyLayer = false;
20953
20954        //var oReturnData = new Object();
20955        if (CAFE24.SHOP_FRONT_PRODUCT_OPTIONLAYER.isExistLayer(true) === true) {
20956            //parent.EC$('html, body').css('overflowY', 'auto');
20957            closeBuyLayer(false);
20958            sLocation = parent.location;
20959            bBuyLayer = true;
20960        }
20961
20962        //프레임으로 선언된 페이지일경우
20963        if (typeof(bIsOptionSelectFrame) !== 'undefined' && bIsOptionSelectFrame === true) {
20964            sLocation = parent.location;
20965            bBuyLayer = true;
20966        }
20967        /*
20968        oReturnData['sLocation'] = sLocation;
20969        oReturnData['bBuyLayer'] = bBuyLayer;
20970        */
20971
20972        oReturnData = sLocation;
20973
20974        if (typeof(sType) === 'boolean') {
20975            oReturnData = bBuyLayer;
20976        }
20977        return oReturnData;
20978    }
20979};
20980
20981CAFE24.SHOP_FRONT_PRODUCT_RESTOCK = (function() {
20982
20983    return {
20984        isRestock: function(sType) {
20985
20986            if (sType === 'sms_restock') {
20987                return true;
20988            }
20989
20990            if (sType === 'email_restock') {
20991                return true;
20992            }
20993
20994            return false;
20995        },
20996        openRestockEmailPopup: function()
20997        {
20998            product_submit('email_restock');
20999        },
21000        bindOpenRestockEmailPopup: function(product_no)
21001        {
21002            action_email_restock(product_no);
21003
21004        }
21005    };
21006})();
21007
21008var EC_SHOP_FRONT_PRODUCT_RESTOCK = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_PRODUCT_RESTOCK');
21009
21010//상세 장바구니 담기확인창에서 스크립트를 중목으로 볼러오는부분을 제거하기위해서 추가
21011//사용자 디자인에서도 basket.js에 있는 함수에 의존적이라서 추가가 안되어있다면 아래 함수들을 실행하도록 함
21012if (typeof(layer_basket_paging) !== 'function') {
21013  //레이어 장바구니 페이징
21014  function layer_basket_paging(page_no)
21015  {
21016      var sUrl = '/product/add_basket2.html?page=' + page_no + '&layerbasket=T';
21017      if (typeof(sBasketDelvType) !== 'undefined') {
21018          sUrl += sUrl + '&delvtype=' + sBasketDelvType;
21019      }
21020
21021      // URL 에 ch_ref 파라미터 추가
21022      if (CAFE24.hasChRef()) {
21023        // 이미 ch_ref가 포함되어 있는지 확인
21024        var urlObj = new URL(sUrl, window.location.origin);
21025        if (!urlObj.searchParams.has('ch_ref')) {
21026            sUrl = CAFE24.attachChRef(sUrl);
21027        }
21028      }
21029
21030      EC$.get(sUrl, '', function(sHtml)
21031      {
21032          sHtml = sHtml.replace(/<script.*?ind-script\/optimizer.php.*?<\/script>/g, '');
21033          EC$('#confirmLayer').html(sHtml);
21034          EC$('#confirmLayer').show();
21035
21036          // set delvtype to basket
21037          try {
21038              EC$(".xans-order-layerbasket").find("a[href='/order/basket.html']").attr("href", "/order/basket.html?delvtype=" + delvtype);
21039          } catch (e) {
21040              PRODUCTSUBMIT.DEBUG.messageOut(e, 'PRODUCT_ACTION.layer_basket_paging');
21041          }
21042          ReferenceCurrencyPrice.init();
21043      });
21044  }
21045}
21046
21047if (typeof(Basket) === 'undefined') {
21048  var Basket = {
21049      orderLayerAll: function(oElem) {
21050          var aParam = {basket_type: 'all_buy'};
21051          var sOrderUrl = EC$(oElem).attr('link-order') || '/order/orderform.html?basket_type='+ aParam.basket_type;
21052
21053          if (sBasketDelvType != "") {
21054              sOrderUrl += '&delvtype=' + sBasketDelvType;
21055          }
21056
21057          // URL 에 ch_ref 파라미터 추가
21058          var sChRef = null;
21059          if (typeof CAFE24 !== 'undefined' && CAFE24.getChRefData && typeof CAFE24.getChRefData === 'function') {
21060              sChRef = CAFE24.getChRefData();
21061          } else {
21062              sChRef = sessionStorage.getItem('ch_ref');
21063          }
21064        
21065          if (sChRef) {
21066              sOrderUrl = sOrderUrl.replace(/[?&]ch_ref=[^&]*/g, '');
21067              var querySeparator = (sOrderUrl.includes('?')) ? '&' : '?';
21068              sOrderUrl += querySeparator + 'ch_ref=' + encodeURIComponent(sChRef);
21069
21070              aParam['ch_ref'] = sChRef;
21071          }
21072
21073          var sLoginUrl = EC$(oElem).attr('link-login') || '/member/login.html';
21074
21075          EC$.post('/exec/front/order/order/', aParam, function(data) {
21076            if (data.result < 0) {
21077                // [ECQAUNIT-75276] 몰 언어 알럿을 선택 언어로 번역 표시 (내부 폴백 포함, CommonUtil은 DispAbstract 공통 로드)
21078                CAPP_SHOP_FRONT_COMMON_UTIL.alertTranslated(data.alertMSG);
21079                return;
21080            }
21081
21082            if (data.isLogin == 'F') { // 비로그인 주문 > 로그인페이지로 이동
21083                sLoginUrl = sLoginUrl + '?noMember=1&returnUrl=' + escape(sOrderUrl);
21084
21085                // URL 에 ch_ref 파라미터 추가
21086                if (CAFE24.hasChRef()) {
21087                  // 이미 ch_ref가 포함되어 있는지 확인
21088                  var urlObj = new URL(sLoginUrl, window.location.origin);
21089                  if (!urlObj.searchParams.has('ch_ref')) {
21090                      sLoginUrl = CAFE24.attachChRef(sLoginUrl);
21091                  }
21092                }
21093
21094                location.href = sLoginUrl;
21095            } else {
21096                // URL 에 ch_ref 파라미터 추가
21097                var sChRef = null;
21098                if (typeof CAFE24 !== 'undefined' && CAFE24.getChRefData && typeof CAFE24.getChRefData === 'function') {
21099                    sChRef = CAFE24.getChRefData();
21100                } else {
21101                    sChRef = sessionStorage.getItem('ch_ref');
21102                }
21103              
21104                if (sChRef) {
21105                    sOrderUrl = sOrderUrl.replace(/[?&]ch_ref=[^&]*/g, '');
21106                    var querySeparator = (sOrderUrl.includes('?')) ? '&' : '?';
21107                    sOrderUrl += querySeparator + 'ch_ref=' + encodeURIComponent(sChRef);
21108                }
21109
21110                location.href = sOrderUrl;
21111            }
21112        }, 'json');
21113      }
21114  };
21115}
21116
21117/**
21118 * 장바구니 유효성 검증 validation
21119 */
21120CAFE24.SHOP_FRONT_BASKET_VALID = {
21121    // 장바구니 상품 중복여부 확인
21122    isBasketProductDuplicateValid: function(sParam) {
21123        var bReturn = true;
21124
21125        EC$.ajax({
21126            url: '/exec/front/order/Basketduplicate/',
21127            type: 'post',
21128            data: sParam,
21129            async: false,
21130            dataType: 'json',
21131            success: function(data) {
21132                if (data.result === true) {
21133                    if (confirm(__('장바구니에 동일한 상품이 있습니다. ' + '\n' + '장바구니에 추가하시겠습니까?')) === false) {
21134                        bReturn = false;
21135                        return false;
21136                    }
21137                }
21138            }
21139        });
21140
21141        return (bReturn !== false);
21142    }
21143};
21144
21145/**
21146 * 카테고리 쿠키정보 세팅
21147 * @param aCookieInfo
21148 */
21149function setCategoryCookie(aCookieInfo)
21150{
21151    // 4차 도메인 대응으로 모바일 대표도메인인 경우 /m 하위 경로로 쿠키 설정
21152    var sPath = EC_ROUTE.getMobile() === true && EC_ROUTE.isBaseDomain() === true ? '/m/product' : '/product';
21153    if (EC$.cookie('ec_category') !== null) {
21154        EC$.cookie('ec_category', null, {'path': sPath});
21155    }
21156
21157    if (aCookieInfo.hasOwnProperty('category_no') === false || aCookieInfo.hasOwnProperty('category_name') === false) {
21158        return;
21159    }
21160
21161    if (typeof aCookieInfo['category_no'] !== 'number' || typeof aCookieInfo['category_name'] !== 'string') {
21162        return;
21163    }
21164
21165    var sCookie = EC$.param(aCookieInfo);
21166    EC$.cookie('ec_category', sCookie, {'path': sPath});
21167}
21168
21169/**
21170 * 자사몰 랜딩 쿠키 세팅
21171 */
21172function setShoppingPayCookie()
21173{
21174    EC$.cookie('ec_provide_shoppingpay', 'T', {'path': '/'});
21175}
21176
21177function setInAppCookie()
21178{
21179    EC$.cookie('co_linktype', 'in_app');
21180}
21181
21182var EC_SHOP_FRONT_BASKET_VALIID = CAFE24.getDeprecatedNamespace('SHOP_FRONT_BASKET_VALID');
21183
21184CAFE24.EC_SHOP_FRONT_BASKET_ACTION_EVENT = (function () {
21185    var sEventName = 'EC_PRODUCT_ACTION_BASKET';
21186    var oParam = {};
21187    var bIsProcess = false;
21188    return {
21189        init: function (iProductNo) {
21190            bIsProcess = true;
21191            oParam = {
21192                mall_id: SHOP.getMallID(),
21193                shop_no: CAFE24.SDE_SHOP_NUM,
21194                product_no: iProductNo,
21195                contents: [],
21196                currency: CAFE24.SHOP_CURRENCY_INFO[CAFE24.SDE_SHOP_NUM].aShopCurrencyInfo.currency_code,
21197            };
21198        },
21199        pushContent: function (oData) {
21200            oParam.contents.push(oData);
21201        },
21202        trigger: function (sParam, aResult) {
21203            var oData = this.queryStringToJSON(sParam);
21204            this.init(oData.product_no);
21205
21206            // [ECHOSTING-623890] 추가할인액 계산(비동기 AJAX) 데이터 미도착/timeout 시 getSaleItem()이 false를 반환하여
21207            // price:0 / variant_code 누락의 빈 페이로드가 발화되고 메타 픽셀 등 구독자가 이를 드롭(간헐 미집계)한다.
21208            // 계산기 미준비면 서버 장바구니 응답 기반 기존 경로(setDataFromParam)로 폴백한다.
21209            if (typeof TotalAddSale !== 'undefined' && this.isTotalAddSaleReady()) {
21210                this.setDataFromTotalAddSale();
21211            } else {
21212                this.setDataFromParam(oData, aResult);
21213            }
21214
21215            if (!bIsProcess) {
21216                EC$('body').trigger(sEventName, oParam);
21217            }
21218        },
21219        isTotalAddSaleReady: function () {
21220            var oProductList = TotalAddSale.getProductList();
21221            if (typeof oProductList === 'undefined' || Object.keys(oProductList).length === 0) {
21222                return false;
21223            }
21224            for (var sItemCode in oProductList) {
21225                if (oProductList.hasOwnProperty(sItemCode) && TotalAddSale.getSaleItem(sItemCode) === false) {
21226                    return false;
21227                }
21228            }
21229            return true;
21230        },
21231        setDataFromTotalAddSale: function () {
21232            var basketProductList = TotalAddSale.getProductList();
21233            if (typeof basketProductList !== 'undefined' && Object.keys(basketProductList).length > 0) {
21234                oParam.category = CAFE24.FRONT_JS_CONFIG_SHOP.oCategoryInfo;
21235                oParam.total_price = TotalAddSale.getTotalOrderPrice();
21236                for (var sItemCode in basketProductList) {
21237                    if (basketProductList.hasOwnProperty(sItemCode)) {
21238                        var oSaleItem = TotalAddSale.getSaleItem(sItemCode);
21239                        // sale 데이터 미계산(바로구매 등) 시 해당 품목 스킵하여 결과 콜백 체인 중단 방지 (ECHOSTING-625233)
21240                        if (oSaleItem === false) {
21241                            continue;
21242                        }
21243                        var sVariantCode = typeof(set_option_data) !== 'undefined' ? item_code : oSaleItem.item_code;
21244                        this.pushContent({
21245                            price: Math.floor(parseFloat(oSaleItem.item_price) || 0),
21246                            variant_code: sVariantCode,
21247                            quantity: parseInt(oSaleItem.quantity, 10) || 1,
21248                            name: product_name
21249                        });
21250                    }
21251                }
21252
21253                bIsProcess = false;
21254            }
21255        },
21256        setDataFromParam: function (oData, aResult) {
21257            if (typeof aResult !== 'undefined' && typeof aResult.aBasketProduct === 'object') {
21258                var iTotalPrice = 0;
21259                for (var i = 0; i < aResult.aBasketProduct.length; i++) {
21260                    var iItemPrice = Math.floor(parseFloat(aResult.aBasketProduct[i].tax_included_product_price) || 0);
21261                    var iQuantity = parseInt(aResult.aBasketProduct[i].quantity, 10) || 1;
21262                    iTotalPrice += iItemPrice * iQuantity;
21263                    this.pushContent({
21264                        variant_code: aResult.aBasketProduct[i].item_code,
21265                        quantity: iQuantity,
21266                        name: aResult.aBasketProduct[i].product_name,
21267                        price: iItemPrice,
21268                    });
21269                }
21270                oParam.total_price = iTotalPrice;
21271
21272                bIsProcess = false;
21273            }
21274        },
21275        getEventData: function () {
21276            // [ECHOSTING-623890] 미발화 결정(bIsProcess=true: 계산기 미준비 + 폴백 데이터 부재) 시 null 반환 →
21277            // relay 송신부(상세/레이어/세트)가 빈 payload를 부모로 보내지 않게 하여,
21278            // 수신부(NewProductOptionSelect/Channel)의 truthy 가드가 빈 이벤트 재발사를 차단한다.
21279            if (bIsProcess) {
21280                return null;
21281            }
21282            return JSON.parse(JSON.stringify(oParam));
21283        },
21284        queryStringToJSON: function (sParam) {
21285            var aParam = decodeURIComponent(sParam).split('&');
21286            var oResult = {};
21287            EC$.each(aParam, function (iKey, sText) {
21288                sText = sText.split('=');
21289                var sName = sText[0];
21290                var sValue = sText[1];
21291                if (sName.length) {
21292                    if (oResult[sName] !== undefined) {
21293                        if (!oResult[sName].push) {
21294                            oResult[sName] = [oResult[sName]];
21295                        }
21296                        oResult[sName].push(sValue || '');
21297                    } else {
21298                        oResult[sName] = sValue || '';
21299                    }
21300                }
21301            });
21302            return oResult;
21303        }
21304    };
21305})();
21306
21307EC$(function () {
21308    CAPP_PRODUCT_LIST_WISHICON.init();
21309});
21310var CAPP_PRODUCT_LIST_WISHICON = {
21311    iDuplicateSecond: 2000, //중복 클릭 제한시간
21312    iClickCount: 0,
21313    iRecentClickProductNo: 0,
21314    iTimeoutId: 0, // 중복방지 대기시간 실행 시퀀스 번호
21315    sCommand: 'add',
21316    bRequesting: false, // 등록/삭제 요청 처리중 여부 (ECHOSTING-631149)
21317    init: function()
21318    {
21319        var iProductNo = 0;
21320        var iCategoryNo = 0;
21321        var oObj = null;
21322        var sLogin = '';
21323        var bIsIndividualSetProduct = false;
21324        try {
21325            EC$(document).on('click', '.ec-product-listwishicon', function () {
21326                oObj = EC$(this);
21327                iProductNo = parseInt(oObj.attr('productno'));
21328                iCategoryNo = oObj.attr('categoryno');
21329                sLogin = oObj.attr('login_status');
21330                bIsIndividualSetProduct = oObj.attr('individual-set') === 'T';
21331                if (oObj.data('ec-redirect') === 'T') {
21332                    location.href = EC$(this).data('ec-redirect-url');
21333                    return;
21334                }
21335
21336                if (sLogin !== 'T') {
21337                    alert(__('로그인 후 관심상품 등록을 해주세요.'));
21338                    location.href = '/member/login.html?returnUrl=' + encodeURIComponent(location.pathname + location.search);
21339                    return;
21340                }
21341
21342
21343                // 이전 요청이 아직 처리중이면 무시 - 응답 전 연타로 같은 add 요청이
21344                // 중복 전송되어 wish_list duplicate key 가 발생하는 것을 방지 (ECHOSTING-631149)
21345                // 클릭 카운트 상태를 바꾸기 전에 확인해야 무시된 클릭이 응답 후 토글 클릭을 막지 않는다
21346                if (CAPP_PRODUCT_LIST_WISHICON.bRequesting === true) {
21347                    return;
21348                }
21349
21350                if (CAPP_PRODUCT_LIST_WISHICON.iRecentClickProductNo === iProductNo) {
21351                    if (CAPP_PRODUCT_LIST_WISHICON.iClickCount === 1) {
21352                        CAPP_PRODUCT_LIST_WISHICON.iClickCount++;
21353                        CAPP_PRODUCT_LIST_WISHICON.initCount();
21354                    } else if (CAPP_PRODUCT_LIST_WISHICON.iClickCount > 1) {
21355                        return;
21356                    }
21357                } else {
21358                    CAPP_PRODUCT_LIST_WISHICON.iClickCount = 0;
21359                    CAPP_PRODUCT_LIST_WISHICON.iRecentClickProductNo = iProductNo;
21360                    if (CAPP_PRODUCT_LIST_WISHICON.iTimeoutId > 0) {
21361                        clearTimeout(CAPP_PRODUCT_LIST_WISHICON.iTimeoutId);
21362                    }
21363                }
21364
21365                // DB 처리전 카운트를 해야 정확히 중복체크가능
21366                CAPP_PRODUCT_LIST_WISHICON.iClickCount++;
21367
21368                if (oObj.attr('icon_status') === 'on') {
21369                    CAPP_PRODUCT_LIST_WISHICON.sCommand = 'del';
21370                } else {
21371                    CAPP_PRODUCT_LIST_WISHICON.sCommand = 'add';
21372                }
21373
21374                var sUrl = '/exec/front/Product/Wishlist/';
21375                var sParam = 'command=' + CAPP_PRODUCT_LIST_WISHICON.sCommand + '&from=wish_icon';
21376                sParam += '&referer=' + encodeURIComponent('//' + location.hostname + location.pathname + location.search);
21377                sParam += '&product_no=' + iProductNo + '&cate_no=' + iCategoryNo;
21378                if (bIsIndividualSetProduct === true) {
21379                    sParam += '&set_product=T';
21380                }
21381
21382                CAPP_PRODUCT_LIST_WISHICON.bRequesting = true;
21383                EC$.ajax({
21384                    type: 'POST',
21385                    url: sUrl,
21386                    data: sParam,
21387                    dataType: 'json',
21388                    success: function (data) {
21389                        CAPP_PRODUCT_LIST_WISHICON.getResultWishIconAjax(data, oObj);
21390                    },
21391                    // 실패 응답에서도 잠금을 해제해야 재시도가 가능하다 (ECHOSTING-631149)
21392                    complete: function () {
21393                        CAPP_PRODUCT_LIST_WISHICON.bRequesting = false;
21394                    }
21395                });
21396            });
21397        } catch (e) {}
21398    },
21399
21400    /**
21401     * 클릭후 시간체크
21402     */
21403    initCount: function()
21404    {
21405        CAPP_PRODUCT_LIST_WISHICON.iTimeoutId = setTimeout(function() {
21406            CAPP_PRODUCT_LIST_WISHICON.iClickCount = 0;
21407        }, CAPP_PRODUCT_LIST_WISHICON.iDuplicateSecond);
21408    },
21409
21410    getResultWishIconAjax: function(aData, oObj)
21411    {
21412        var STORAGE_KEY = 'localWishList' + CAFE24.SDE_SHOP_NUM;
21413        if (aData == null) return;
21414        if (aData.result == 'SUCCESS') {
21415            var iProductNo = EC$(oObj).attr('productno');
21416
21417            EC$('.ec-product-listwishicon[productno="'+iProductNo+'"]').each(function() {
21418                var oStorageData = CAFE24.UTIL.parseJSON(sessionStorage.getItem(STORAGE_KEY));
21419                if (EC$(this).attr('icon_status') === 'off') {
21420                    var src = aData.data.wish_icon.on;
21421                    var alt = aData.data.wish_alt.on;
21422                    if (oStorageData !== null && oStorageData.hasOwnProperty('on_tags') === true) {
21423                        src = oStorageData.on_tags.src;
21424                        alt = oStorageData.on_tags.alt;
21425                    }
21426                    EC$(this).attr('src', src);
21427                    EC$(this).attr('alt', alt);
21428                    EC$(this).attr('icon_status', 'on');
21429                } else {
21430                    var src = aData.data.wish_icon.off;
21431                    var alt = aData.data.wish_alt.off;
21432                    if (oStorageData !== null && oStorageData.hasOwnProperty('off_tags') === true) {
21433                        src = oStorageData.off_tags.src;
21434                        alt = oStorageData.off_tags.alt;
21435                    }
21436                    EC$(this).attr('src', src);
21437                    EC$(this).attr('alt', alt);
21438                    EC$(this).attr('icon_status', 'off');
21439                }
21440            });
21441
21442            if (CAPP_ASYNC_METHODS.hasOwnProperty('WishList') === true) {
21443                // 관심상품 추가/삭제시 sessionStorage 추가/삭제 처리
21444                CAPP_ASYNC_METHODS.WishList.setSessionStorageItem(iProductNo, CAPP_PRODUCT_LIST_WISHICON.sCommand);
21445            }
21446
21447            if (CAPP_ASYNC_METHODS.hasOwnProperty('Wishcount') === true) {
21448                CAPP_ASYNC_METHODS.Wishcount.restoreCache();
21449                CAPP_ASYNC_METHODS.Wishcount.execute();
21450            }
21451
21452            if (CAPP_PRODUCT_LIST_WISHICON.sCommand === 'add') { // 위시리스트 등록시에만 실행
21453                CAFE24.PLUSAPP_BRIDGE.addWishList(iProductNo);
21454                if (typeof CAFE24.KAKAO_PIXEL_BRIDGE !== 'undefined') {
21455                    CAFE24.KAKAO_PIXEL_BRIDGE.addWishList(iProductNo);
21456                }
21457            }
21458
21459        } else if (aData.result == 'ERROR') {
21460            alert(__('실패하였습니다.'));
21461        } else if (aData.result == 'NOT_LOGIN') {
21462            alert(__('회원 로그인 후 이용하실 수 있습니다.'));
21463        } else if (aData.result == 'INVALID_REQUEST') {
21464            alert(__('파라미터가 잘못되었습니다.'));
21465        }
21466    }
21467};
21468
21469/**
21470 * 문화세 적용 여부를 설정하고 반환하는 객체
21471 */
21472var SHOP_FRONT_PRODUCT_CULTURAL_TAX = {
21473    is_cultural_tax: 'F',
21474
21475    /**
21476     * 문화세 적용 여부를 반환합니다.
21477     * @returns {string} 'T' 또는 'F'로 문화세 적용 여부
21478     */
21479    getIsCulturalTax: function() {
21480        return CAFE24.FRONT_JS_CONFIG_SHOP.is_cultural_tax || this.is_cultural_tax;
21481    }
21482};
21483var STOCKLAYER = (function() {
21484
21485    var sUrl = '/product/stocklayer.html';
21486
21487    //세트 상품 여부
21488    function isSetProdct()
21489    {
21490        if (typeof(set_option_data) === 'undefined') {
21491            return false;
21492        }
21493
21494        return true;
21495    }
21496
21497    //모든 재고 레이어 Element Get
21498    function getAllStockLayer()
21499    {
21500        return EC$('.ec-shop-detail-stock-layer');
21501    }
21502
21503    return {
21504        init: function() {
21505            try {
21506                EC$(document).on('click', 'a[name="EC-stockdesign"]', function(e) {
21507                    e.preventDefault();
21508                    var iProductNo = EC$(this).attr('product_no');
21509                    var sPageType = EC$(this).attr('page_type');
21510                    STOCKLAYER.closeStockLayer();
21511
21512                    if (EC$(this).parent().find('.ec-shop-detail-stock-layer').length == 0) {
21513                        var oParam = {};
21514
21515                        oParam['product_no'] = iProductNo;
21516                        oParam['page_type'] = sPageType;
21517
21518                        var oHtml = EC$('<div>');
21519                        oHtml.addClass('ec-shop-detail-stock-layer');
21520                        EC$(this).parent().append(oHtml);
21521                        EC$.ajax({
21522                            type: 'GET',
21523                            url: sUrl,
21524                            data: oParam,
21525                            success: function (sHtml) {
21526                                sHtml = sHtml.replace(/[<]script( [^ ]+)? src=\"[^>]*>([\s\S]*?)[<]\/script>/g, "");
21527                                oHtml.html(sHtml);
21528                            },
21529                            error: function (e) {
21530                                __('오류발생');
21531                            }
21532                        });
21533                    } else {
21534                        EC$(this).parent().find('.ec-shop-detail-stock-layer').show();
21535                    }
21536
21537                    e.preventDefault();
21538                });
21539            } catch (e) {}
21540        },
21541
21542        closeStockLayer: function() {
21543            var $oAllStockLayer = getAllStockLayer();
21544            $oAllStockLayer.hide();
21545        }
21546    };
21547})();
21548
21549EC$(function() {
21550    STOCKLAYER.init();
21551});
21552
21553EC$(function()
21554{
21555    // 패싯의 검색 엘리먼트 액션
21556    EC$('#ec-product-searchdata-form .xans-product-searchfilterlist .ec-product-searchdata-form').click(function()
21557    {
21558        if (CAFE24.FRONT_PRODUCT_SEARCH_DATA.setFacetSelector(EC$(this)) === false) {
21559            return false;
21560        }
21561        if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.isIncludeFixedForm() === true) {
21562            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchListCheck(EC$(this));
21563            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchCountShow();
21564         } else {
21565            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21566        }
21567    });
21568
21569     // 버튼식 금액 찾기
21570    EC$('.xans-product-searchfilterlist .ec-product-searchdata-price').click(function()
21571    {
21572        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setButtonSearchPrice(EC$(this));
21573    });
21574
21575    EC$('#ec-product-searchdata-mobile-button').click(function() {
21576        EC$(".xans-product-searchdata > form").show();
21577        EC$("#container").css("height",EC$(".xans-product-searchdata").height());
21578        EC$('.xans-product-searchdata.typeSlide').addClass('open');
21579
21580        EC$('.xans-product-searchfilterlist').each(function() {
21581            var iChecked = EC$(this).find('input.ec-product-searchdata-form:checked').length;
21582            var iSelected = EC$(this).find('input.ec-product-searchdata-form:selected').length;
21583
21584            var iWithin = 0;
21585            var sWithin = EC$(this).find('.withinInput input[type="text"]').val();
21586
21587            if (typeof(sWithin) !== 'undefined') {
21588                iWithin = sWithin.length;
21589            }
21590
21591            if (iChecked + iSelected + iWithin > 0) {
21592                EC$(this).addClass('checked');
21593            }
21594
21595            if (iChecked > 0) {
21596                EC$(this).find('input.ec-product-searchdata-form:checked').parent().addClass('selected');
21597            }
21598
21599            if (iSelected > 0) {
21600                EC$(this).find('input.ec-product-searchdata-form:selected').parent().addClass('selected');
21601            }
21602        });
21603
21604        CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.setLayerMoreFilter(false);
21605    });
21606    EC$('.xans-product-searchdata .btnClose').click(function() {
21607        EC$('#ec-product-searchdata-form').hide();
21608        EC$('#container').css('height','');
21609        EC$('#ec-product-searchdata-form').find('.xans-product-searchdata.typeSlide').removeClass('open');
21610
21611        CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.setLayerMoreFilter(true);
21612    });
21613
21614    // form input에 걸려있는 submit 기본 이벤트 제거
21615    EC$(document).on('keyup keypress', '#ec-product-searchdata-form .withinInput input[type="text"]', function(event) {
21616        if (event.keyCode === 13) {
21617            event.preventDefault();
21618        }
21619    });
21620
21621    EC$(document).on('click touchstart', '#ec-product-searchdata-submit', function() {
21622        CAFE24.FRONT_PRODUCT_SEARCH_DATA.bSearchSubmit = false;
21623        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21624    });
21625
21626    EC$(document).on('click', '.btnSearchDelete', function() {
21627
21628        if (CAFE24.FRONT_PRODUCT_SEARCH_DATA.bSearchSubmit === true) {
21629            return false;
21630        }
21631        var sStype = EC$(this).attr('search_type');
21632        var sSvalue = EC$(this).attr('search_value');
21633
21634        if (sStype === 'ePriceSearch') {
21635            EC$('#ec-product-searchdata-form #ec-product-searchdata-minval').val(0);
21636            EC$('#ec-product-searchdata-form #ec-product-searchdata-maxval').val(0);
21637            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = 0;
21638            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = 0;
21639        } else if (sStype === 'eWithinSearch') {
21640            if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bFixedListForm === true) {
21641                EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .withinInput input[type="text"]').val('');
21642            }
21643            EC$('#ec-product-searchdata-form .xans-product-searchfilterlist .withinInput input[type="text"]').val('');
21644        } else {
21645            EC$('#ec-product-searchdata-form .ec-product-searchdata-form.ec_search_selected').each(function() {
21646                if (sSvalue === EC$(this).attr('sValue')) {
21647                    EC$(this).removeClass('ec_search_selected');
21648                    EC$(this).prop('checked',false);
21649                    return false;
21650                }
21651            });
21652            EC$('#ec-product-searchdata-form .ec-product-searchdata-form:checked').each(function() {
21653                if (sSvalue === EC$(this).val()) {
21654                    EC$(this).removeClass('ec_search_selected');
21655                    EC$(this).prop('checked', false);
21656                    return false;
21657                }
21658            });
21659        }
21660
21661        CAFE24.FRONT_PRODUCT_SEARCH_DATA.bSearchSubmit = false;
21662        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21663
21664    });
21665
21666    EC$('.ec-product-searchdata-reset').click(function() {
21667        EC$('#ec-product-searchdata-form .ec-product-searchdata-form.ec_search_selected').removeClass('ec_search_selected');
21668        EC$('#ec-product-searchdata-form .ec-product-searchdata-form:checked').prop('checked', false);
21669        EC$('.xans-product.xans-product-searchoption').removeClass('selected');
21670        EC$('.xans-product-searchprice li').removeClass('selected');
21671        EC$('#ec-product-searchdata-form #ec_mobile_brand_0').removeClass('checked');
21672        EC$('#ec-product-searchdata-form #ec_mobile_price_0').removeClass('checked');
21673
21674        EC$('#ec-product-searchdata-form #ec-product-searchdata-minval').val('');
21675        EC$('#ec-product-searchdata-form #ec-product-searchdata-maxval').val('');
21676        CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = 0;
21677        CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = 0;
21678
21679        EC$('#ec-product-searchdata-form .xans-product-searchfilterlist .withinInput input[type="text"]').val('');
21680
21681        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21682    });
21683
21684    EC$('.btn_order').click(function() {
21685        EC$('#order_by').val(EC$(this).attr('rel'));
21686        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21687    });
21688
21689    // 검색옵션 버튼 클릭시 레이어팝업 노출
21690    EC$(".btnSearchOption").click(function() {
21691        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchLayerShow();
21692    });
21693    // 검색옵션 버튼 클릭시 레이어팝업 노출
21694    EC$("#ec-product-searchoption > .close").click(function() {
21695        EC$("#ec-product-searchoption").hide();
21696    });
21697
21698    // 검색옵션 버튼 클릭시 레이어팝업 submit
21699    EC$("#ec-product-searchdata-layer-submit").click(function() {
21700        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchLayerSubmit();
21701    });
21702
21703    // 검색옵션 버튼 클릭시 레이어팝업 reset
21704    EC$("#ec-product-searchdata-layer-reset").click(function() {
21705        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchLayerShow();
21706        EC$('#ec-product-searchdata-detail-type').prop('checked', false);
21707        EC$('#ec-product-searchdata-except-keyword').val('');
21708    });
21709
21710    EC$("#ec-product-searchdata-submit_button").click(function() {
21711        if (CAFE24.UTIL.trim(EC$('#ec-product-searchdata-calendar','.xans-product').val()) === '' && CAFE24.UTIL.trim(EC$('#ec-product-searchdata-keyword').val()) === '') {
21712            if (CAFE24.UTIL.trim(EC$('#ec-product-searchdata-keyword').val()) === '') {
21713                alert(__('검색어를 입력해주세요'));
21714                EC$('#ec-product-searchdata-keyword').focus();
21715                return false;
21716            }
21717        }
21718        if (EC$(this).attr('type') === 'button') {
21719            EC$('#ec-product-searchdata-searchkeyword_form').submit();
21720        }
21721    });
21722
21723    // 기존 select 요소에 대한 처리
21724    EC$('select#order_by').change(function() {
21725        var selectedValue = EC$(this).find('option:selected').val();
21726        CAFE24.FRONT_PRODUCT_SEARCH_DATA.updateOrderBy(selectedValue);
21727        return false;
21728    });
21729
21730    // 새로운 ul 요소에 대한 처리
21731    EC$('#order_by li').click(function(e) {
21732        e.preventDefault();
21733        var selectedValue = EC$(this).data('value');
21734        CAFE24.FRONT_PRODUCT_SEARCH_DATA.updateOrderBy(selectedValue);
21735        return false;
21736    });
21737    //
21738    EC$("#ec-product-searchdata-detail-keyword").off('keypress.ec-keyword-event');
21739    EC$("#ec-product-searchdata-detail-keyword , #ec-search-except-keyword").keypress(function(e) {
21740        if (e.keyCode == 13) {
21741            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchLayerSubmit();
21742        }
21743    });
21744    // order by set
21745    EC$("#ec-product-searchdata-searchorderby").change(function()
21746    {
21747        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit(EC$(this).val());
21748    });
21749
21750    // 상세검색 sidebar 슬라이딩
21751    EC$("#ec-searchdata-button").click(function() {
21752        var oTarget = EC$(this).parent();
21753        var oAttrbute = new Object();
21754        var dWidth = '0';
21755
21756        if (oTarget.hasClass('opened') === true) {
21757            dWidth = '-231px';
21758        }
21759        if (EC$('#searchSidebar').hasClass('gRight') === true) {
21760            oAttrbute.right = dWidth;
21761        } else {
21762            oAttrbute.left = dWidth;
21763        }
21764
21765        oTarget.animate(oAttrbute);
21766
21767        if (oTarget.hasClass('opened') === true) {
21768            oTarget.removeClass("opened");
21769        } else {
21770            oTarget.addClass("opened");
21771        }
21772    });
21773    // 상세검색 제목 클릭시 토글 액션
21774    EC$(".ec-searchdata-option-title").click(function() {
21775        if (EC$('#searchContent').length > 0) {
21776            //EC$(this).parent().parent().parent().parent().toggleClass("selected");
21777            EC$('.xans-product.xans-product-searchfilterlist').eq(EC$(this).attr('filterNumber')).toggleClass("selected");
21778
21779        } else {
21780            EC$(this).parent().toggleClass("selected");
21781        }
21782
21783    });
21784    // 검색폼 내 버튼 클릭시 selected 클래스 추가
21785    EC$(".ec-searchdata-option-button, .ec-searchdata-colorchip").click(function() {
21786        if (EC$(this).parent().hasClass("disabled") == false) {
21787            EC$(this).parent().toggleClass("selected");
21788        }
21789    });
21790    // 상품판매가격 버튼 클릭시 selected 클래스 추가
21791    EC$(".ec-searchdata-price-button").click(function() {
21792        EC$(this).parent().siblings().removeClass('selected');
21793        EC$(this).parent().addClass('selected');
21794    });
21795
21796    EC$('#ec-product-searchdata-minval , #ec-product-searchdata-maxval').blur(function() {
21797        if (EC$(this).val() !== '' && CAFE24.FRONT_PRODUCT_SEARCH_DATA.isDecimal2(EC$(this).val()) === false) {
21798            alert(__('금액은 숫자만 입력가능합니다.'));
21799            EC$(this).val('');
21800            return false;
21801        }
21802
21803        if (EC$(this).attr('id') === 'ec-product-searchdata-minval') {
21804            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = EC$(this).val();
21805        } else {
21806            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = EC$(this).val();
21807        }
21808
21809        EC$(this).val(CAFE24.SHOP_PRICE.toShopPrice(EC$(this).val(), false));
21810    });
21811
21812    CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchinit();
21813    CAFE24.FRONT_PRODUCT_SEARCH_DATA_SLIDE.setSearchSlideinit();
21814
21815    // 상품판매가격 버튼 클릭시 selected 클래스 추가
21816    EC$(".btnAllExtend").click(function() {
21817        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setOpenFilterList(EC$(this));
21818    });
21819
21820    // 결과 내 검색 Enter 이벤트
21821    EC$('.xans-product-searchfilterlist .withinInput input.keyword').keypress(function(e) {
21822        if (e.keyCode === 13 && EC$(this).attr('onkeyup') !== '') {
21823            EC$('.withinInput button.btnResearch').click();
21824        }
21825    });
21826
21827    // 결과 내 검색 자동완성 레이어 클릭 처리
21828    EC$(document).on('click', '.xans-product-searchfilterlist .suggest li', function(e) {
21829        // li 내 a 의 기본 이동(bare keyword)을 막아 JS 재검색과의 이중 호출(같은 초 재검색 → 속도제한 alert) 방지 (ECHOSTING-630978)
21830        e.preventDefault();
21831        EC$(this).closest('.withinInput').find('input[type="text"]').val(EC$(this).text());
21832        EC$(this).closest('ul').hide();
21833
21834        if (typeof(mobileWeb) === 'undefined' || mobileWeb !== true) {
21835            EC$('.withinInput button.btnResearch').click();
21836        }
21837    });
21838
21839    // 결과 내 검색 버튼 클릭 시 처리
21840    // 값이 비어있으면 alert 없이 아무것도 처리하지 않음
21841    EC$('.withinInput button.btnResearch').click(function() {
21842
21843        var sWithin = EC$(this).closest('.withinInput').find('input[type="text"]').val();
21844
21845        if (typeof(sWithin) !== 'undefined') {
21846            // 앞/뒤 공백 및 멀티 스페이스 제거
21847            sWithin = CAFE24.UTIL.trim(sWithin.replace(/\s{2,}/g, ' '));
21848
21849            if (sWithin !== '') {
21850                // 실제로 값이 존재하는 경우에 처리
21851                // 셀럭터는 btnResearch + withinInput 등이 세트로 묶여있기 때문에 별도의 ID로 지정할 필요 없음
21852                EC$(this).closest('.withinInput').find('input[type="text"]').val(sWithin);
21853
21854                // IE에서 이벤트 타이밍 문제로 실제 input의 값이 바뀌기 전에 submit 되는 버그가 있어 타임아웃 설정
21855                setTimeout(function() {
21856                    CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
21857                }, 100);
21858            }
21859        }
21860    });
21861});
21862
21863// window load 이후 검색 레이어 스크롤 처리
21864EC$(window).on('load', function() {
21865    CAFE24.FRONT_PRODUCT_SEARCH_DATA.setScrollCheck();
21866});
21867
21868// window resize 이후 검색 레이어 스크롤 처리
21869EC$(window).resize(function() {
21870    CAFE24.FRONT_PRODUCT_SEARCH_DATA.setScrollCheck();
21871});
21872
21873CAFE24.FRONT_PRODUCT_SEARCH_DATA = {
21874    bSearchSubmit: false,
21875    bEmptyElasticData: false,
21876    bUseSearch: 'F',
21877    iSearchSetMinPrice: 0,
21878    iSearchSetMaxPrice: 0,
21879    iSearchFormCount: 0,
21880    sShowOpen: 'F',
21881    sFilterShowNumList: '',
21882    aOpenFilterElement: [],
21883    /**
21884     * EC JAPAN에서 CAFE24.FRONT_PRODUCT_SEARCH_DATA.sShowOpen 값을 세팅
21885     */
21886    setShowOpen: function()
21887    {
21888        if (this.getCommonObjectValue('is_facet_open') !== true) {
21889            return;
21890        }
21891        this.sShowOpen = 'T';
21892    },
21893    /**
21894     * EC JAPAN에서 CAFE24.FRONT_PRODUCT_SEARCH_DATA.aOpenFilterElement 값을 세팅
21895     */
21896    setFilterShowNumList: function()
21897    {
21898        var aOpenFacetElement = this.getCommonObjectValue('open_facet_element');
21899        if (aOpenFacetElement === null) {
21900            return;
21901        }
21902        if (aOpenFacetElement.length === 0) {
21903            return;
21904        }
21905        this.aOpenFilterElement = this.getCommonObjectValue('open_facet_element');
21906    },
21907    /**
21908     * EC JAPAN에서 CAFE24.FRONT_PRODUCT_SEARCH_DATA.bUseSearch 값을 세팅
21909     */
21910    setUseSearch: function()
21911    {
21912        if (this.getCommonObjectValue('V') === 6) {
21913            this.bUseSearch = this.getCommonObjectValue('bIsFacetSelected') === true ? 'T' : 'F';
21914        }
21915    },
21916    /**
21917     * EC JAPAN에서 Elastic 검색을 사용하지 않는데 디자인 반영한 경우 불필요한 영역을 제거
21918     */
21919    setSearchOptionButton: function()
21920    {
21921        if (this.getCommonObjectValue('V') === 6) {
21922            if (CAFE24.FRONT_PRODUCT_SEARCH_DATA.getCommonObjectValue('bIsElasticInUse') === true) {
21923                return true;
21924            }
21925            EC$('#ec-product-searchdata-mobile-button').remove();
21926            EC$('#ec-product-searchoption').remove(); // 검색옵션 레이어
21927            EC$('#ec-product-searchdata-searchkeyword_form').find('.btnSearchOption').remove(); // 검색옵션 버튼
21928        } else {
21929            if (typeof(bUseElastic) === 'undefined' || bUseElastic === false) {
21930                EC$('#ec-product-searchdata-searchkeyword_form').find('.btnSearchOption').remove();
21931            }
21932        }
21933    },
21934    /**
21935     * Facet을 제거해야하는지 판단하여 제거
21936     */
21937    setFacetClear: function()
21938    {
21939        if (this.getCommonObjectValue('V') !== 6) {
21940            return true;
21941        }
21942        var bIsFacetClear = this.getCommonObjectValue('bIsFacetClear');
21943        if (bIsFacetClear === true) {
21944            this.setNoDataList();
21945            EC$('#ec-product-searchdata-mobile-button').remove();
21946            EC$('.xans-product.xans-product-searchfilterlist').remove();
21947            EC$('#ec-searchdata-area').remove();
21948        }
21949        if (CAFE24.FRONT_PRODUCT_SEARCH_DATA.getCommonObjectValue('bIsElasticInUse') !== true) {
21950            EC$('#ec-product-searchdata-mobile-button').remove();
21951        }
21952    },
21953    /**
21954     * 초기화
21955     */
21956    setSearchinit: function() {
21957        this.setShowOpen();
21958        this.setFilterShowNumList();
21959        this.setSearchOptionButton();
21960        this.setFacetClear();
21961        this.setUseSearch();
21962        CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.setSearchFixListinit();
21963        if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.isIncludeFixedForm() === true) {
21964            if (this.bUseSearch === 'T') {
21965                EC$('.xans-product-searchconditiondata').show();
21966            }
21967
21968            if (EC$('.xans-product-searchconditiondata').length > 0) {
21969                EC$('#ec-product-searchdata-mobile-button').addClass('checked');
21970            }
21971
21972            EC$('.xans-product.xans-product-searchfilterlist').each(function() {
21973                if (EC$(this).find('.ec-product-searchdata-form.ec_search_selected').length > 0) {
21974                    var oTargetObject = EC$(this).find('.ec-product-searchdata-form.ec_search_selected').eq(0);
21975                    CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchListCheck(oTargetObject);
21976                }
21977            });
21978
21979            if (EC$('.ec-product-searchdata-price.ec_search_selected').length > 0) {
21980                CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchListCheck(EC$('.ec-product-searchdata-price.ec_search_selected').eq(0));
21981            }
21982
21983            EC$('.xans-product-searchdata .searchInput').find('button.btnDelete').on('click', function() {
21984                if (EC$(this).closest('.withinInput').length === 0) {
21985                    var oForm = EC$(this).parents('form');
21986                    EC$('#ec-product-searchdata-keyword').val('').focus();
21987                    EC$('body').removeClass("eMobilePopup"); // eMobilePopup 클래스 삭제
21988                    EC$('body').css("width", ""); // width:100% 삭제
21989                    oForm.find('#ec-product-searchdata-keyword_drop,autoList').hide();
21990                    oForm.find('#ec-product-searchdata-auto-list').hide();
21991                }
21992            });
21993
21994            EC$('.xans-product-searchfilterlist .withinInput .btnDelete').click(function() {
21995                EC$(this).closest('.withinInput').find('input[type="text"]').val('').focus();
21996                EC$(this).closest('.xans-product-searchfilterlist').removeClass('checked');
21997            });
21998
21999            EC$('#ec-product-searchdata-use').click(function() {
22000                EC$(".xans-product-searchdata > form").show();
22001                EC$(this).addClass('checked');
22002                EC$("#container").css("height",EC$(".xans-product-searchdata").height());
22003            });
22004            if (this.bEmptyElasticData === true) {
22005                EC$('#ec-product-searchdata-mobile-button').remove();
22006            }
22007        } else {
22008            if (this.bEmptyElasticData === true) {
22009                EC$('.xans-product.xans-product-searchfilterlist').remove();
22010                EC$('#ec-searchdata-area').remove();
22011            } else {
22012                // 고정형 열기 닫기
22013                if (EC$('#searchContent').length > 0) {
22014                    this.setExtendButton();
22015
22016                    if (this.sShowOpen === 'T') {
22017                        EC$('.btnAllExtend').addClass('open');
22018                    }
22019                }
22020            }
22021
22022            // 좌측/우측 슬라이딩 형일때만
22023            if (mobileWeb === false && EC$('#searchSidebar').length > 0) {
22024                EC$('.xans-product.xans-product-searchfilterlist.selected').each(function () {
22025                    CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchFormCount += EC$(this).find('button').length;
22026                });
22027            }
22028
22029            if (EC$('.xans-product.xans-product-searchfilterlist').length < 6) {
22030                EC$('.btnAllExtend').hide();
22031            }
22032        }
22033        EC$('.ec-product-searchdata-form-clear').click(function()
22034        {
22035            var sKeyValue = EC$(this).attr('filter_key_value');
22036            var sSelectedFilterKey = 'ec-product-searchdata-' + sKeyValue;
22037            var sSelectFilterFormClass = 'ec-product-searchdata-form';
22038            var oTarget = EC$('#ec-product-searchdata-form .' + sSelectedFilterKey).parent();
22039            if (sKeyValue === 'price') {
22040                EC$('#ec-product-searchdata-form #ec-product-searchdata-minval').val('');
22041                EC$('#ec-product-searchdata-form #ec-product-searchdata-maxval').val('');
22042                CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = 0;
22043                CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = 0;
22044                sSelectFilterFormClass = 'ec-product-searchdata-price';
22045
22046                CAFE24.FRONT_PRODUCT_SEARCH_DATA_SLIDE.setSearchClear();
22047            }
22048            if (oTarget.find('.' + sSelectFilterFormClass).prop('type') === 'checkbox') {
22049                oTarget.find('.' + sSelectFilterFormClass).each(function() {
22050                    if (EC$(this).is(':checked') === true) {
22051                        EC$(this).click();
22052                    }
22053                });
22054            } else {
22055                oTarget.find('.xans-product-filterform li').removeClass('selected');
22056                oTarget.find('.xans-product-filterform .ec-product-searchdata-form').removeClass('ec_search_selected');
22057            }
22058
22059            oTarget.removeClass('checked');
22060
22061            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchCountShow();
22062        });
22063
22064        // 드롭다운 초기 텍스트 설정
22065        if (EC$('.mDropDown').length > 0) {
22066            var selectedText = EC$('.mDropDown .option li.selected a').text();
22067            if (selectedText) {
22068                EC$('.mDropDown .value span').text(selectedText);
22069            }
22070        }
22071    },
22072    /**
22073     * 버튼식 금액 찾기
22074     * @param object 선택된 object
22075     */
22076    setButtonSearchPrice: function(oSearchPirce)
22077    {
22078        EC$('#ec-product-searchdata-form  .ec-product-searchdata-price').parent().find('.xans-product-filterform  li').removeClass('selected');
22079
22080        oSearchPirce.parents('li').addClass('selected');
22081
22082        if (oSearchPirce.hasClass('ec_search_selected') === true) {
22083            this.iSearchSetMinPrice = null;
22084            this.iSearchSetMaxPrice = null;
22085
22086            this.setSearchSubmit();
22087        }
22088
22089        var iMinPrice = oSearchPirce.attr('min_price');
22090        var iMaxPrice = oSearchPirce.attr('max_price');
22091
22092
22093        EC$('#ec-product-searchdata-form  #ec-product-searchdata-minval').val(CAFE24.SHOP_PRICE.toShopPrice(iMinPrice, false));
22094        EC$('#ec-product-searchdata-form  #ec-product-searchdata-maxval').val(CAFE24.SHOP_PRICE.toShopPrice(iMaxPrice, false));
22095
22096        this.iSearchSetMinPrice = iMinPrice;
22097        this.iSearchSetMaxPrice = iMaxPrice;
22098
22099        if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.isIncludeFixedForm() === true) {
22100            this.setSearchListCheck(oSearchPirce);
22101            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchCountShow();
22102        } else {
22103            this.setSearchSubmit();
22104        }
22105
22106    },
22107    /**
22108     * 실제 submit처리
22109     */
22110    setSearchSubmit: function(sSortType, sFormName) {
22111
22112        if (this.bSearchSubmit === true) {
22113            return false;
22114        }
22115        // 중복 클릭 방지
22116        this.bSearchSubmit = true;
22117
22118        if (sFormName === undefined || sFormName === '') {
22119            sFormName = '#ec-product-searchdata-form ';
22120        }
22121        this.setFacetFormParam(sFormName, sSortType);
22122
22123        // pc fixed form
22124        this.setTopFixedSearchForm();
22125
22126        var sPathName = location.pathname;
22127        var bSearchAll = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('search_all');
22128        // 메인페이지에서 패싯 필터링 변경시 검색 페이지로 이동
22129        if ((sPathName === '/' || sPathName === '/index.html') || bSearchAll === 'T') {
22130            sPathName = '/product/search.html';
22131            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="search_all" value="T">');
22132        }
22133
22134        EC$('#ec-product-searchdata-form').attr('action', sPathName);
22135        EC$('#ec-product-searchdata-form').submit();
22136    },
22137    /**
22138     * 소숫점 둘째자리 체크
22139     */
22140    isDecimal2: function(value) {
22141        if (value == '') return false;
22142        return (/^[0-9]+(\.[0-9]{1,2})?$/).test(value);
22143    },
22144
22145    setSearchListCheck: function (oSearchId)
22146    {
22147        var sValue = oSearchId.attr('sValue');
22148
22149        if (oSearchId.prop('type') === 'checkbox') {
22150            sValue = oSearchId.val();
22151        }
22152
22153        if (sValue === '' || typeof(sValue) === 'undefined') {
22154            return;
22155        }
22156
22157        var aFilterDataValue = sValue.split('=');
22158        var sSelectedFilterKey = 'ec-product-searchdata-' + aFilterDataValue[0];
22159        var oTarget = EC$('.' + sSelectedFilterKey).parent();
22160
22161        if (aFilterDataValue[0] === 'price') {
22162            oTarget = EC$('.title.' + sSelectedFilterKey).parent();
22163        }
22164
22165        if (oSearchId.hasClass('circle') === true) {
22166            if (oSearchId.find('.icoColorCheck').length === 0) {
22167                oSearchId.html('<span class="icoColorCheck"></span>');
22168            }
22169        }
22170
22171        var bCheckSelected = false;
22172
22173        if (oSearchId.closest('.xans-product-filterform').find('input:checkbox').length > 0) {
22174            if (oSearchId.closest('.xans-product-filterform').find('label.selected').length > 0) {
22175                bCheckSelected = true;
22176            }
22177        } else {
22178            if (oSearchId.closest('.xans-product-filterform').find('li.selected').length > 0) {
22179                bCheckSelected = true;
22180            }
22181        }
22182        if (bCheckSelected === true) {
22183            oTarget.addClass('checked');
22184        } else {
22185            oTarget.removeClass('checked');
22186        }
22187    },
22188    // 모바일 더보기 용
22189    setSearchPriceData: function () {
22190
22191        var iMinPriceValue = this.iSearchSetMinPrice;
22192        var iMaxPriceValue = this.iSearchSetMaxPrice;
22193        var bPriceCheck = false;
22194        if (iMinPriceValue !== 0 || iMaxPriceValue !== 0) {
22195            bPriceCheck = true;
22196        }
22197
22198        if (bPriceCheck === true) {
22199            EC$('#ec-product-searchdata-minval').val(iMinPriceValue);
22200            EC$('#ec-product-searchdata-maxval').val(iMaxPriceValue);
22201        }
22202    },
22203    /**
22204     * facet처리시
22205     */
22206    setSearchCountShow: function()
22207    {
22208        if (EC$('#ec-product-searchdata-search-count').length <= 0) {
22209            return;
22210        }
22211        if (this.bSearchSubmit === true) {
22212            return;
22213        }
22214        EC$('#ec-product-searchdata-form input:hidden[name^="search_form[option_data]"]').each(function() {
22215            EC$(this).remove();
22216        });
22217
22218        var sAction = '/exec/front/product/Searchcount';
22219
22220        this.setFacetFormParam('#ec-product-searchdata-form ');
22221
22222        if (EC$('#ec-product-searchdata-catenum').val() === '') {
22223            var iCateNum = CAFE24.FRONT_PRODUCT_SEARCH_DATA.getCommonObjectValue('iCatgoryNum');
22224            EC$('#ec-product-searchdata-catenum').val(iCateNum);
22225        }
22226
22227        EC$.post(sAction, EC$('#ec-product-searchdata-form').serialize(), function(aResultJson)
22228        {
22229            var sMsg = __('SEARCH.COUNT', 'SHOP.FRONT.NEW.PRODUCT.SEARCHDATA').replace('$s', CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.iLayerFormTotalCount);
22230            if (aResultJson['bPassed'] === true) {
22231                sMsg = __('SEARCH.COUNT', 'SHOP.FRONT.NEW.PRODUCT.SEARCHDATA').replace('$s', aResultJson['iCount']);
22232            }
22233            EC$('#ec-product-searchdata-submit').html(sMsg);
22234
22235        }, 'json');
22236
22237        this.bSearchSubmit = false;
22238    },
22239    /**
22240     * 실제 submit처리
22241     */
22242    setSearchLayerSubmit: function() {
22243
22244        var sKeyword = EC$('#ec-product-searchdata-detail-keyword').val();
22245        var sExceptKeyword = EC$('#ec-product-searchdata-except-keyword').val();
22246
22247        if (CAFE24.UTIL.trim(sKeyword) === '') {
22248            alert(__('검색어를 입력해주세요'));
22249            EC$('#ec-product-searchdata-detail-keyword').focus();
22250            return false;
22251        }
22252        EC$("#ec-product-searchoption").hide();
22253        EC$('#ec-product-searchdata-detail-keyword').val(encodeURIComponent(sKeyword));
22254
22255        if (sExceptKeyword !== '') {
22256            EC$('#ec-product-searchdata-except-keyword').val(encodeURIComponent(sExceptKeyword));
22257        }
22258
22259        EC$('#ec-product-searchdata-keyword').attr('name','');
22260        EC$('#ec-product-searchdata-detail-keyword').attr('name','keyword');
22261        EC$('<input>').attr({
22262            'type': 'hidden',
22263            'name': 'search_from_layer'
22264        }).val('T').appendTo(EC$('#ec-product-searchdata-searchkeyword_form'));
22265
22266        EC$('#ec-product-searchdata-searchkeyword_form').attr('action',location.pathname);
22267        EC$('#ec-product-searchdata-searchkeyword_form').submit();
22268
22269    },
22270    setSearchLayerShow: function() {
22271        var sSearchType = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('search_detail_type');
22272        var sExceptKeyword = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('except_keyword');
22273
22274        EC$('#ec-product-searchdata-detail-keyword').val(EC$('#ec-product-searchdata-keyword').val());
22275        if (sExceptKeyword !== '') {
22276            EC$('#ec-product-searchdata-except-keyword').val(decodeURIComponent(sExceptKeyword));
22277        }
22278        if (sSearchType !== '') {
22279            EC$('#ec-product-searchdata-detail-type').prop('checked',true);
22280        }
22281
22282        EC$("#ec-product-searchoption").show();
22283    },
22284    setNoDataList: function () {
22285        EC$("#ec-searchdata-button").remove();
22286    },
22287    /**
22288     * 검색 레이어 스크롤 처리
22289     * CSS로 알아서 처리되므로, 해당 element에 높이 값만 넣어줌
22290     */
22291    setScrollCheck: function() {
22292        var oSearchdataArea = EC$('#searchSidebar #ec-searchdata-area');
22293
22294        if (oSearchdataArea.length > 0) {
22295            oSearchdataArea.height(EC$(window).height() - parseInt(oSearchdataArea.css('padding-top'), 10) - parseInt(oSearchdataArea.css('padding-bottom'), 10));
22296        }
22297    },
22298    setOpenFilterList: function (oObject) {
22299        if (oObject.hasClass('open') === false) {
22300            EC$('.xans-product.xans-product-searchfilterlist').show();
22301            this.sShowOpen = 'T';
22302            this.setExtendButton();
22303        } else {
22304            EC$('.xans-product.xans-product-searchfilterlist').each(function(i) {
22305                if (i > 4) {
22306                    EC$(this).hide();
22307                }
22308            });
22309            this.sShowOpen = 'F';
22310        }
22311        oObject.toggleClass('open');
22312    },
22313    setExtendButton: function()
22314    {
22315        // 각 항목별 높이값이 37이상인 경우 2줄로 나옴 이때 펼치기버튼 노출 비노출
22316        EC$('.xans-product.xans-product-searchfilterlist').each(function(i) {
22317            var oTargetObject = EC$(this).find('.xans-product-filterform');
22318            if (oTargetObject.height() < 38) {
22319                oTargetObject.parent().find('.extend').hide(); //('fase');
22320            } else {
22321                oTargetObject.parent().find('.extend').show(); //('fase');
22322                oTargetObject.parent().find('.ec-searchdata-option-title').attr('filterNumber', i);
22323            }
22324        });
22325
22326        if (this.sFilterShowNumList !== '' || this.aOpenFilterElement.length > 0) {
22327            var aFilterShowList = this.sFilterShowNumList.split(',');
22328            if (this.aOpenFilterElement.length > 0) {
22329                aFilterShowList = this.aOpenFilterElement;
22330            }
22331            for (var i = 0; i < aFilterShowList.length; i++) {
22332                if (aFilterShowList[i] !== '') {
22333                    EC$('.xans-product.xans-product-searchfilterlist').eq(aFilterShowList[i]).toggleClass("selected");
22334                }
22335            }
22336        }
22337
22338        // 가격 영역별 검색의 경우 xans-product-filterform.content 가 존재하지 않음 이때 슬라이더가 있으면 해당 버튼 삭제필요
22339        if (EC$('.xans-product.xans-product-searchfilterlist .priceSlide').length > 0) {
22340            EC$('.xans-product.xans-product-searchfilterlist .priceSlide').find('.extend').hide();
22341        }
22342
22343        // 결과 내 검색인 경우에도 무조건 '+' 버튼 삭제
22344        if (EC$('.xans-product.xans-product-searchfilterlist .searchForm').length > 0) {
22345            EC$('.xans-product.xans-product-searchfilterlist .searchForm').find('.extend').hide();
22346        }
22347    },
22348    getCommonObjectValue: function(sPropertyName)
22349    {
22350        if (typeof(EC_FRONT_JS_CONFIG_CURATION) !== 'object') {
22351            return null;
22352        }
22353        if (EC_FRONT_JS_CONFIG_CURATION.hasOwnProperty(sPropertyName) === false) {
22354            return null;
22355        }
22356        return EC_FRONT_JS_CONFIG_CURATION[sPropertyName];
22357    },
22358    setFacetSelector: function(oObjectSelector)
22359    {
22360        if (oObjectSelector.prop('type') !== 'checkbox') {
22361            if (oObjectSelector.parent().hasClass('disabled') === true) {
22362                return false;
22363            }
22364        } else {
22365            if (oObjectSelector.parent().parent().hasClass('disabled') === true) {
22366                return false;
22367            }
22368        }
22369        var oTarget = oObjectSelector;
22370        if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.isIncludeFixedForm() === true) {
22371            oTarget = oObjectSelector.parent();
22372        }
22373        if (oTarget.hasClass('ec_search_selected') === true || oTarget.hasClass('selected')) {
22374            oTarget.removeClass('selected');
22375            oObjectSelector.removeClass('ec_search_selected');
22376            // oObjectSelector.attr('sValue
22377        } else {
22378            if (oObjectSelector.hasClass('ec-product-searchdata-price') === false) {
22379                oTarget.addClass('selected');
22380            }
22381            oObjectSelector.addClass('ec_search_selected');
22382        }
22383
22384    } ,
22385    setFacetFilterValue: function(oTargetForm)
22386    {
22387        EC$(oTargetForm + '.ec-product-searchdata-form.ec_search_selected').each(function() {
22388            if (EC$(this).prop('type') !== 'checkbox') {
22389                var sValueData = EC$(this).attr('sValue');
22390                if (sValueData !== undefined && sValueData !== '' && CAFE24.FRONT_PRODUCT_SEARCH_DATA.isDuplicateValueData(sValueData) === false) {
22391                    EC$('<input>').attr({type: 'hidden',name: 'search_form[option_data][]',value: encodeURIComponent(sValueData)}).appendTo('#ec-product-searchdata-form');
22392                }
22393            }
22394        });
22395    },
22396    isDuplicateValueData: function(sValue) {
22397        var bDuplicate = false;
22398        EC$('#ec-product-searchdata-form input:hidden[name^="search_form[option_data]"]').each(function() {
22399            if (EC$(this).val() === sValue) {
22400                bDuplicate = true;
22401                return false;
22402            }
22403        });
22404
22405        return bDuplicate;
22406    },
22407
22408    setFacetFormParam: function(sFormName, sSortType) {
22409
22410        this.setFacetFilterValue(sFormName);
22411
22412        var iCateNum = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('cate_no');
22413        if (EC$('#ec-product-searchdata-catenum').length === 0) {
22414            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="cate_no" id="ec-product-searchdata-catenum" value="'+ iCateNum + '"/>');
22415        } else {
22416            EC$('#ec-product-searchdata-catenum').val(iCateNum);
22417        }
22418
22419        var sSortMethod = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('sort_method');
22420        if (sSortType !== undefined && sSortType !== '') {
22421            sSortMethod = sSortType;
22422        }
22423        if (sSortMethod !== '' && sSortMethod.search('#Product_ListMenu') < 0) {
22424            sSortMethod = sSortMethod + '#Product_ListMenu';
22425        }
22426
22427        if (sSortMethod !== '') {
22428            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="sort_method" id="ec-product-searchdata-sort_method" value="'+ sSortMethod + '"/>');
22429        }
22430        //EC$('#ec-product-searchdata-sort_method').val(sSortMethod);
22431
22432        var iMinPriceValue = this.iSearchSetMinPrice;
22433        var iMaxPriceValue = this.iSearchSetMaxPrice;
22434
22435        if (iMinPriceValue !== 0) {
22436            EC$(sFormName + '#ec-product-searchdata-minval').val(iMinPriceValue);
22437        }
22438
22439        if (iMaxPriceValue !== 0) {
22440            EC$(sFormName + '#ec-product-searchdata-maxval').val(iMaxPriceValue);
22441        }
22442
22443        var sExceptKeyword = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('except_keyword');
22444        var sSearchDetailType = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('search_detail_type');
22445        var sSearchType = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('search_type');
22446
22447        if (sExceptKeyword !== '') {
22448            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="except_keyword" value="'+ sExceptKeyword + '"/>');
22449        }
22450
22451        if (sSearchDetailType !== '') {
22452            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="search_detail_type" value="'+ sSearchDetailType + '"/>');
22453        }
22454
22455        if (sSearchType !== '') {
22456            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="search_type" value="'+ sSearchType + '"/>');
22457        }
22458
22459        var sKeywordValue = CAPP_SHOP_FRONT_COMMON_UTIL.getParameterByName('keyword');
22460        if (EC$('#ec-product-searchdata-keyword').length > 0) {
22461            if (sKeywordValue === '' && EC$('#ec-product-searchdata-keyword').val() !== '') {
22462                sKeywordValue = encodeURIComponent(EC$('#ec-product-searchdata-keyword').val());
22463            }
22464            EC$('#ec-product-searchdata-keyword_hidden').val(sKeywordValue);
22465        } else if (sKeywordValue !== '') {
22466            // 기존 스킨을 사용하는 경우에도 동작 할수 있도록
22467            EC$('#ec-product-searchdata-keyword_hidden').val(sKeywordValue);
22468        }
22469
22470        var sWithin = '';
22471
22472        if (EC$('.withinInput input[type="text"]:visible').length > 0) {
22473            sWithin = EC$('.withinInput input[type="text"]:visible').val();
22474        } else {
22475            sWithin = EC$('.ec-product-searchdata-within').parent().find('.withinInput input[type="text"]').val();
22476        }
22477
22478        if (typeof(sWithin) !== 'undefined') {
22479            sWithin = CAFE24.UTIL.trim(sWithin.replace(/\s{2,}/g, ' '));
22480
22481            if (sWithin.length > 0) {
22482                EC$('#ec-product-searchdata-form').append('<input type="hidden" name="within" value="' + sWithin + '"/>');
22483            }
22484        }
22485    },
22486    setTopFixedSearchForm: function() {
22487        if (EC$('#searchContent').length === 0) {
22488            return;
22489        }
22490        // 상단형일때 전체 오픈 여부
22491        EC$("#ec-product-searchdata-form").append('<input type="hidden" name="sShowOpen" value="' + this.sShowOpen + '"/>');
22492
22493        var sFilterShowNumList = '';
22494        // 몇번째 필터를 열었는지 보관
22495        EC$('.xans-product.xans-product-searchfilterlist.selected').each(function() {
22496            sFilterShowNumList += EC$(this).attr('filterlistnum') + ',';
22497        });
22498
22499        if (EC$('.xans-product.xans-product-searchfilterlist.selected').length > 0) {
22500            EC$("#ec-product-searchdata-form").append('<input type="hidden" name="sFilterShowNumList" value="' + sFilterShowNumList + '"/>');
22501        }
22502
22503    },
22504    updateOrderBy: function(selectedValue) {
22505        var aUrl = location.href.split('?');
22506
22507        if (selectedValue) {
22508            if (aUrl.length < 2 || aUrl[1].indexOf('order_by') == -1) {
22509                window.location.href = location.href + (aUrl.length < 2 ? '?' : '&') + 'order_by=' + selectedValue;
22510            } else {
22511                var aParam = aUrl[1].split('&');
22512                for (var i = 0; i < aParam.length; i++) {
22513                    if (aParam[i].indexOf('order_by') > -1) {
22514                        aParam[i] = 'order_by=' + selectedValue;
22515                    }
22516                }
22517                location.href = aUrl[0] + '?' + aParam.join('&');
22518            }
22519        }
22520    }
22521
22522};
22523
22524var EC_FRONT_PRODUCT_SEARCH_DATA = CAFE24.getDeprecatedNamespace('EC_FRONT_PRODUCT_SEARCH_DATA');
22525
22526CAFE24.FRONT_PRODUCT_SEARCH_DATA_SLIDE = {
22527    iSearchSetMinPrice: 0,
22528    iSearchSetMaxPrice: 0,
22529    iSearchParamMinPrice: 0,
22530    iSearchParamMaxPrice: 0,
22531    oSlider: null,
22532    oCloneSlider: null,
22533    /**
22534     * V6에서 사용하는 가격 세팅 메소드
22535     * @param oConfig
22536     */
22537    setPriceRangeConfig: function()
22538    {
22539        var oPriceRange = CAFE24.FRONT_PRODUCT_SEARCH_DATA.getCommonObjectValue('price_range');
22540        if (oPriceRange === null) {
22541            return false;
22542        }
22543        this.iSearchSetMinPrice = oPriceRange.range_min;
22544        this.iSearchSetMaxPrice = oPriceRange.range_max;
22545        this.iSearchParamMinPrice = oPriceRange.search_min;
22546        this.iSearchParamMaxPrice = oPriceRange.search_max;
22547    },
22548
22549    setSearchSlideinit: function() {
22550        this.setPriceRangeConfig();
22551        if (EC$("#eSearchPriceSlider").length > 0) {
22552
22553            var eSearchPriceSlider = document.getElementById('eSearchPriceSlider');
22554            if (this.iSearchSetMinPrice > 0 || this.iSearchSetMaxPrice > 0) {
22555                noUiSlider.create(eSearchPriceSlider, {
22556                    start: [this.iSearchSetMinPrice, this.iSearchSetMaxPrice],
22557                    connect: true,
22558                    range: {
22559                        'min': this.iSearchSetMinPrice,
22560                        'max': this.iSearchSetMaxPrice
22561                    }
22562                });
22563                this.setInitPriceSet(eSearchPriceSlider, '#ec-product-searchdata-form ');
22564                this.cloneSlider();
22565                eSearchPriceSlider.noUiSlider.on('change', function(values) {
22566                    CAFE24.FRONT_PRODUCT_SEARCH_DATA_SLIDE.setSlideAction(values, false);
22567                });
22568            }
22569            this.oSlider = eSearchPriceSlider.noUiSlider;
22570        }
22571    },
22572    setInitPriceSet: function (eSearchPriceSlider, oTargetForm) {
22573        EC$(oTargetForm + '#eMinSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(this.iSearchSetMinPrice));
22574        EC$(oTargetForm + '#eMaxSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(this.iSearchSetMaxPrice));
22575
22576        if (this.iSearchParamMinPrice > 0 || this.iSearchParamMaxPrice > 0) {
22577            eSearchPriceSlider.noUiSlider.set([this.iSearchParamMinPrice, this.iSearchParamMaxPrice]);
22578
22579            EC$(oTargetForm + '#eMinSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(this.iSearchParamMinPrice));
22580            EC$(oTargetForm + '#eMaxSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(this.iSearchParamMaxPrice));
22581
22582            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = this.iSearchParamMinPrice;
22583            CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = this.iSearchParamMaxPrice;
22584        }
22585    },
22586    setSlideAction: function (aValues, bForceAction) {
22587        var iMinSlidePirce = aValues[0];
22588        var iMaxSlidePirce = aValues[1];
22589
22590        EC$('#eMinSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(iMinSlidePirce));
22591        EC$('#eMaxSlidePriceView').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(iMaxSlidePirce));
22592
22593
22594        CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = iMinSlidePirce;
22595        CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = iMaxSlidePirce;
22596
22597
22598        if (bForceAction === false && CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bFixedListForm === false && (EC_MOBILE === false && EC_MOBILE_DEVICE === false)) {
22599            bForceAction = true;
22600        }
22601
22602        if (bForceAction === true) {
22603            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
22604        } else {
22605            EC$('.ec-product-searchdata-price').parent().addClass('checked');
22606            CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchCountShow();
22607        }
22608    },
22609    setSearchClear: function () {
22610        if (EC$("#eSearchPriceSlider").length > 0) {
22611            this.oSlider.set([this.iSearchParamMinPrice, this.iSearchParamMaxPrice]);
22612        }
22613        if (EC$('#ec-product-fixed-slider').length > 0) {
22614            this.oCloneSlider.set([this.iSearchParamMinPrice, this.iSearchParamMaxPrice]);
22615        }
22616
22617    },
22618    cloneSlider: function () {
22619        if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bFixedListForm === false || EC$('#ec-product-fixed-slider').length === 0) {
22620            return;
22621        }
22622        var oNewSearchPriceSlider = document.getElementById('ec-product-fixed-slider');
22623
22624        noUiSlider.create(oNewSearchPriceSlider, {
22625            start: [this.iSearchSetMinPrice, this.iSearchSetMaxPrice],
22626            connect: true,
22627            range: {
22628                'min': this.iSearchSetMinPrice,
22629                'max': this.iSearchSetMaxPrice
22630            }
22631        });
22632
22633        this.setInitPriceSet(oNewSearchPriceSlider, '#ec-product-fixed-form ');
22634
22635        oNewSearchPriceSlider.noUiSlider.on('change', function(values) {
22636            CAFE24.FRONT_PRODUCT_SEARCH_DATA_SLIDE.setSlideAction(values, true);
22637        });
22638
22639        this.oCloneSlider = oNewSearchPriceSlider.noUiSlider;
22640    }
22641};
22642
22643var EC_FRONT_PRODUCT_SEARCH_DATA_SLIDE = CAFE24.getDeprecatedNamespace('EC_FRONT_PRODUCT_SEARCH_DATA_SLIDE');
22644
22645CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM = {
22646    bFixedListForm: false,
22647    bFixedFormAction: false,
22648    bLayerFormInit: false,
22649    bLayerFormOpen: false,
22650    iLayerFormTotalCount: 0,
22651    setSearchFixListinit: function() {
22652        if (EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist').length > 0 && EC$('#ec-product-fixed-form').length > 0) {
22653            this.bFixedListForm = true;
22654
22655            // 패싯의 검색 엘리먼트 액션
22656            EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .ec-product-searchdata-form').click(function()
22657            {
22658                if (CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bLayerFormOpen === true) {
22659                    EC$('#ec-product-searchdata-form .xans-product-searchfilterlist').find('#' + EC$(this).attr('id')).click();
22660                    return false;
22661                }
22662
22663                CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.setFixedFormSubmit(EC$(this));
22664            });
22665            EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .ec-product-searchdata-price').click(function()
22666            {
22667                CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMinPrice = EC$(this).attr('min_price');
22668                CAFE24.FRONT_PRODUCT_SEARCH_DATA.iSearchSetMaxPrice = EC$(this).attr('max_price');
22669                CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bFixedFormAction = true;
22670                CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit();
22671            });
22672
22673            EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .ec-product-searchdata-form').each(function() {
22674                if (EC$(this).prop('type') === 'checkbox') {
22675                    var sObjectId = EC$(this).attr('id');
22676
22677                    var oLabelObject = EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .ec-product-searchdata-form').find("label[for='" + sObjectId + "']");
22678
22679                    if (oLabelObject.length > 0) {
22680                        oLabelObject.attr('id' , sObjectId + 'fixed');
22681                        EC$(this).attr('id' , sObjectId + 'fixed');
22682                    }
22683                }
22684            });
22685
22686            EC$('#ec-product-fixed-form .xans-product-fixedsearchfilterlist .btnDelete').click(function() {
22687                EC$(this).closest('.withinInput').find('input[type="text"]').val('').focus();
22688            });
22689
22690            var sMsg = __('SEARCH.COUNT', 'SHOP.FRONT.NEW.PRODUCT.SEARCHDATA').replace('$s', CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.iLayerFormTotalCount);
22691            EC$('#ec-product-searchdata-submit').html(sMsg);
22692        }
22693    },
22694
22695    setFixedFormSubmit: function(oObject) {
22696        EC$('#ec-product-searchdata-form input:hidden[name^="search_form[option_data]"]').each(function() {
22697            EC$(this).remove();
22698        });
22699        CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.bFixedFormAction = true;
22700        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setFacetSelector(oObject);
22701        CAFE24.FRONT_PRODUCT_SEARCH_FIX_LIST_FORM.setFixedFormData();
22702        CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchSubmit('' , '#ec-product-fixed-form ');
22703    },
22704
22705    setFixedFormData: function() {
22706        if (this.bFixedFormAction === true) {
22707            EC$('#ec-product-fixed-form .ec-product-searchdata-form:checked').each(function() {
22708                EC$('<input>').attr({type: 'hidden',name: 'search_form[option_data][]',value: encodeURIComponent(EC$(this).val())}).appendTo('#ec-product-searchdata-form');
22709            });
22710        }
22711    } ,
22712    isIncludeFixedForm: function() {
22713        if (this.bFixedListForm === true || EC_MOBILE === true || EC_MOBILE_DEVICE === true) {
22714            return true;
22715        }
22716
22717        return false;
22718    },
22719    setLayerMoreFilter: function (bClose) {
22720        if (this.bFixedListForm === false) {
22721            return;
22722        }
22723        if (bClose === true) {
22724            EC$('#ec-product-fixed-form-filter').hide();
22725            this.bLayerFormOpen = false;
22726        } else {
22727            EC$('#ec-product-fixed-form-filter').show();
22728
22729            this.bLayerFormOpen = true;
22730            if (this.bLayerFormInit === false && CAFE24.FRONT_PRODUCT_SEARCH_DATA.bUseSearch === 'T') {
22731                CAFE24.FRONT_PRODUCT_SEARCH_DATA.setSearchCountShow();
22732                this.bLayerFormInit = true;
22733            }
22734        }
22735    },
22736};
22737
22738var EC_FRONT_PRODUCT_SEARCH_FIX_LIST_FORM = CAFE24.getDeprecatedNamespace('EC_FRONT_PRODUCT_SEARCH_FIX_LIST_FORM');
22739
22740/**
22741 * 목록 > 상품 좋아요.
22742 */
22743CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT = {
22744    bIsReady: false, // 좋아요 클릭준비완료 여부.
22745    bIsSetEvent: false, // 좋아요 버튼 이벤트 지정 여부.
22746    aImgSrc: [], // 좋아요(On/Off) 아이콘 경로.
22747    aImgAlt: [], // 좋아요(On/Off) 아이콘 Alt태그
22748    aMyLikePrdNo: [], // 유저가 이미 좋아요 선택한 상품번호
22749    oMyshopLikeCntNode: null, // layout_shopingInfo 좋아요 span 노드
22750
22751    // 상품 좋아요 초기화
22752    init: function() {
22753        // 상품 좋아요 사용안함시
22754        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.aConfig.bIsUseLikeProduct !== true) {
22755            return;
22756        }
22757
22758        // ajax 유저가 이미 좋아요 선택한 상품번호 얻기 + 아이콘세팅
22759        this.setLoadData();
22760    },
22761
22762    // 유저가 이미 좋아요 선택한 상품번호 얻기 + 아이콘세팅
22763    setLoadData: function() {
22764        if (EC$('.likePrdIcon').count < 1) {
22765            return;
22766        }
22767
22768        var self = this;
22769
22770        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getMyLikeProductNoInList(function(aData) {
22771            self.aImgSrc = aData.imgSrc;
22772            self.aImgAlt = aData.imgAlt;
22773            self.aMyLikePrdNo = aData.rows;
22774
22775            // 아이콘(on) 세팅
22776            self.setMyLikeProductIconOn();
22777
22778            // 좋아요 클릭 이벤트핸들러 지정
22779            if (self.bIsSetEvent === false) {
22780                self.setEventHandler();
22781                self.bIsSetEvent = true;
22782            }
22783        }, function() {
22784            self.bIsReady = true;
22785        });
22786    },
22787
22788    // 페이지 로드시 유저가 좋아요한 상품 On.아이콘으로 변경
22789    setMyLikeProductIconOn: function() {
22790        var aData = this.aMyLikePrdNo;
22791
22792        for (var i=0; i < aData.length; i++) {
22793            // selected 스타일 적용
22794            EC$(".likePrd_" + aData[i].product_no).each(function() {
22795                EC$(this).addClass('selected');
22796            });
22797
22798            // 아이콘 이미지경로 변경
22799            EC$(".likePrdIcon[product_no='" + aData[i].product_no + "']").each(function() {
22800                EC$(this).attr({'src': CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgSrc.on, 'icon_status': 'on', 'alt': CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgAlt.on});
22801            });
22802        }
22803    },
22804
22805    // 이벤트핸들러 지정
22806    setEventHandler: function() {
22807        // 좋아요 아이콘 클릭 이벤트
22808        try {
22809            EC$(document).on('click', '.likePrd', CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.clickLikeIcon);
22810            var sContext = '';
22811            if (typeof(PREVIEWPRDOUCT) === 'undefined') {
22812                sContext = window.parent.document;
22813            }
22814        } catch (e) {}
22815
22816
22817        // 마이쇼핑 > 상품좋아요 페이지
22818        if (EC$(".xans-myshop-likeproductlist", sContext).length > 0) {
22819            // 팝업 확대보기창 닫기 이벤트
22820            if (EC$(".xans-product-zoompackage").length > 0) {
22821                EC$('.xans-product-zoompackage div.close').on('click', CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.closeZoomReload);
22822            }
22823        }
22824    },
22825
22826    // 좋아요 아이콘 클릭 이벤트핸들러
22827    clickLikeIcon: function() {
22828        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.bIsReady === false) {
22829            return;
22830        }
22831
22832        // 클릭한 상품의 좋아요수, 아이콘 정보얻기
22833        var iPrdNo = EC$('.likePrdIcon', this).attr('product_no');
22834        var iCateNo = EC$('.likePrdIcon', this).attr('category_no');
22835        var sIconStatus= EC$('.likePrdIcon', this).attr('icon_status');
22836        // 카운트 string > int 형으로 변환 (ECHOSTING-260504)
22837        var iLikeCount = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getNumericRemoveCommas(EC$('.likePrdCount', this).text());
22838
22839        // 아이콘경로 및 좋아요수 증감처리
22840        var sNewImgSrc = sNewIconStatus = "";
22841        var iNewLikeCount = 0;
22842        var oLikeWrapNode = EC$(".likePrd_" + iPrdNo);
22843
22844        if (sIconStatus === 'on') {
22845            sNewIconStatus = 'off';
22846            sNewImgSrc = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgSrc.off;
22847            sNewImgAlt = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgAlt.off;
22848            if (iLikeCount > 0) {
22849                iNewLikeCount = --iLikeCount;
22850            }
22851
22852            oLikeWrapNode.each(function() {
22853                EC$(this).removeClass('selected');
22854            });
22855        } else {
22856            sNewIconStatus = 'on';
22857            sNewImgSrc = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgSrc.on;
22858            sNewImgAlt = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.aImgAlt.on;
22859            iNewLikeCount = ++iLikeCount;
22860
22861            // 동일상품 selected 스타일적용
22862            oLikeWrapNode.each(function() {
22863                EC$(this).addClass('selected');
22864            });
22865        }
22866        // 좋아요 카운트 number_format (ECHOSTING-260504)
22867        iNewLikeCount = CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.getNumberFormat(iNewLikeCount);
22868        // 상단.layout_shopingInfo 좋아요수 업데이트
22869        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.updateShopInfoCount(sNewIconStatus);
22870
22871        // 좋아요 아이콘이미지 + 좋아요수 업데이트
22872        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.updateLikeIconCount(iPrdNo, sNewImgSrc, sNewIconStatus, iNewLikeCount, sNewImgAlt);
22873
22874        // ajax 호출 좋아요수(상품) + 마이쇼핑 좋아요 저장
22875        CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.submitMyLikeProduct(iPrdNo, iCateNo, sNewIconStatus);
22876
22877        // 확대보기 팝업에서 좋아요 클릭시, 부모프레임 좋아요 업데이트
22878        if (EC$(".xans-product-zoompackage").length > 0) {
22879            window.parent.CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.updateShopInfoCount(sNewIconStatus);
22880            window.parent.CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.updateLikeIconCount(iPrdNo, sNewImgSrc, sNewIconStatus, iNewLikeCount);
22881        }
22882    },
22883
22884    // 마이쇼핑 > 상품좋아요 목록 > 팝업 확대보기창 닫기 이벤트핸들러
22885    closeZoomReload: function() {
22886        var sIconsStatus = EC$('.xans-product-zoompackage .likePrdIcon').attr('icon_status');
22887
22888        // 팝업에서 좋아요를 취소했으면 좋아요 목록 새로고침
22889        if (sIconsStatus === 'off') {
22890            window.parent.location.reload();
22891        }
22892    },
22893
22894    // 좋아요 아이콘이미지 + 좋아요수 업데이트
22895    updateLikeIconCount: function(iPrdNo, sImgSrc, sIconStatus, iLikeCount, sNewImgAlt) {
22896        // 클릭한 동일상품 아이콘 변경
22897        EC$(".likePrdIcon[product_no='" + iPrdNo + "']").each(function() {
22898            EC$(this).attr({'src': sImgSrc, 'icon_status': sIconStatus, 'alt': sNewImgAlt});
22899        });
22900
22901        // 클릭한 동일상품 좋아요수 변경
22902        EC$('.likePrdCount_' + iPrdNo).each(function() {
22903            EC$(this).text(iLikeCount);
22904        });
22905    },
22906
22907    // 상단.layout_shopingInfo 좋아요수 업데이트
22908    updateShopInfoCount: function(sIconStatus) {
22909        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.oMyshopLikeCntNode === null) {
22910            CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.oMyshopLikeCntNode = EC$('#xans_myshop_like_prd_cnt');
22911        }
22912
22913        var iMyshopLikeCnt;
22914        if (CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.oMyshopLikeCntNode !== null) {
22915            iMyshopLikeCnt = parseInt(EC$(CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.oMyshopLikeCntNode).text());
22916            iMyshopLikeCnt = (sIconStatus === 'on') ? iMyshopLikeCnt + 1 : iMyshopLikeCnt - 1;
22917            iMyshopLikeCnt = (iMyshopLikeCnt < 0 || isNaN(iMyshopLikeCnt)) ? 0 : iMyshopLikeCnt;
22918            CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.oMyshopLikeCntNode.text(iMyshopLikeCnt + '개');
22919        }
22920
22921        if (EC$('#xans_myshop_main_like_prd_cnt').length > 0 && iMyshopLikeCnt >= 0) {
22922            EC$('#xans_myshop_main_like_prd_cnt').text(iMyshopLikeCnt);
22923        }
22924    },
22925
22926    // 상품 좋아요수 + 마이쇼핑 좋아요 저장
22927    submitMyLikeProduct: function(iPrdNo, iCateNo, sIconStatus) {
22928        if (sIconStatus === 'on') {
22929            this.aMyLikePrdNo.push(iPrdNo);
22930        } else {
22931            this.aMyLikePrdNo.pop(iPrdNo);
22932        }
22933
22934        EC$.ajax({
22935            url: '/exec/front/shop/LikeCommon',
22936            type: 'get',
22937            data: {
22938                'mode': 'saveMyLikeProduct',
22939                'iPrdNo': iPrdNo,
22940                'iCateNo': iCateNo,
22941                'sIconStatus': sIconStatus
22942            },
22943            dataType: 'json',
22944            success: function(oReturn) {
22945                if (oReturn.bResult === true) {
22946                    CAFE24.SHOP_FRONT_NEW_LIKE_COMMON.purgeMyLikeProductNoInList();
22947                }
22948            },
22949            complete: function() {
22950                CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.bIsReady = true;
22951            }
22952        });
22953    }
22954};
22955
22956var EC_SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT');
22957
22958EC$(function() {
22959    CAFE24.SHOP_FRONT_NEW_LIKE_COMMON_PRODUCT.init(); // 상품 좋아요.
22960});
22961
22962/**
22963 * jQuery JSON Plugin
22964 * version: 2.3 (2011-09-17)
22965 *
22966 * This document is licensed as free software under the terms of the
22967 * MIT License: http://www.opensource.org/licenses/mit-license.php
22968 *
22969 * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
22970 * website's http://www.json.org/json2.js, which proclaims:
22971 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
22972 * I uphold.
22973 *
22974 * It is also influenced heavily by MochiKit's serializeJSON, which is
22975 * copyrighted 2005 by Bob Ippolito.
22976 */
22977
22978(function($) {
22979
22980    var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
22981        meta = {
22982            '\b': '\\b',
22983            '\t': '\\t',
22984            '\n': '\\n',
22985            '\f': '\\f',
22986            '\r': '\\r',
22987            '"': '\\"',
22988            '\\': '\\\\'
22989        };
22990
22991    /**
22992     * jQuery.toJSON
22993     * Converts the given argument into a JSON respresentation.
22994     *
22995     * @param o {Mixed} The json-serializble *thing* to be converted
22996     *
22997     * If an object has a toJSON prototype, that will be used to get the representation.
22998     * Non-integer/string keys are skipped in the object, as are keys that point to a
22999     * function.
23000     *
23001     */
23002    $.toJSON = typeof JSON === 'object' && JSON.stringify
23003        ? JSON.stringify
23004        : function(o) {
23005
23006        if (o === null) {
23007            return 'null';
23008        }
23009
23010        var type = typeof o;
23011
23012        if (type === 'undefined') {
23013            return undefined;
23014        }
23015        if (type === 'number' || type === 'boolean') {
23016            return '' + o;
23017        }
23018        if (type === 'string') {
23019            return $.quoteString(o);
23020        }
23021        if (type === 'object') {
23022            if (typeof o.toJSON === 'function') {
23023                return $.toJSON(o.toJSON());
23024            }
23025            if (o.constructor === Date) {
23026                var month = o.getUTCMonth() + 1,
23027                    day = o.getUTCDate(),
23028                    year = o.getUTCFullYear(),
23029                    hours = o.getUTCHours(),
23030                    minutes = o.getUTCMinutes(),
23031                    seconds = o.getUTCSeconds(),
23032                    milli = o.getUTCMilliseconds();
23033
23034                if (month < 10) {
23035                    month = '0' + month;
23036                }
23037                if (day < 10) {
23038                    day = '0' + day;
23039                }
23040                if (hours < 10) {
23041                    hours = '0' + hours;
23042                }
23043                if (minutes < 10) {
23044                    minutes = '0' + minutes;
23045                }
23046                if (seconds < 10) {
23047                    seconds = '0' + seconds;
23048                }
23049                if (milli < 100) {
23050                    milli = '0' + milli;
23051                }
23052                if (milli < 10) {
23053                    milli = '0' + milli;
23054                }
23055                return '"' + year + '-' + month + '-' + day + 'T' +
23056                    hours + ':' + minutes + ':' + seconds +
23057                    '.' + milli + 'Z"';
23058            }
23059            if (o.constructor === Array) {
23060                var ret = [];
23061                for (var i = 0; i < o.length; i++) {
23062                    ret.push($.toJSON(o[i]) || 'null');
23063                }
23064                return '[' + ret.join(',') + ']';
23065            }
23066            var name,
23067                val,
23068                pairs = [];
23069            for (var k in o) {
23070                type = typeof k;
23071                if (type === 'number') {
23072                    name = '"' + k + '"';
23073                } else if (type === 'string') {
23074                    name = $.quoteString(k);
23075                } else {
23076                    // Keys must be numerical or string. Skip others
23077                    continue;
23078                }
23079                type = typeof o[k];
23080
23081                if (type === 'function' || type === 'undefined') {
23082                    // Invalid values like these return undefined
23083                    // from toJSON, however those object members
23084                    // shouldn't be included in the JSON string at all.
23085                    continue;
23086                }
23087                val = $.toJSON(o[k]);
23088                pairs.push(name + ':' + val);
23089            }
23090            return '{' + pairs.join(',') + '}';
23091        }
23092    };
23093
23094    /**
23095     * jQuery.evalJSON
23096     * Evaluates a given piece of json source.
23097     *
23098     * @param src {String}
23099     */
23100    $.evalJSON = typeof JSON === 'object' && JSON.parse
23101        ? JSON.parse
23102        : function(src) {
23103        return eval('(' + src + ')');
23104    };
23105
23106    /**
23107     * jQuery.secureEvalJSON
23108     * Evals JSON in a way that is *more* secure.
23109     *
23110     * @param src {String}
23111     */
23112    $.secureEvalJSON = typeof JSON === 'object' && JSON.parse
23113        ? JSON.parse
23114        : function(src) {
23115
23116        var filtered =
23117            src
23118            .replace(/\\["\\\/bfnrtu]/g, '@')
23119            .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
23120            .replace(/(?:^|:|,)(?:\s*\[)+/g, '');
23121
23122        if (/^[\],:{}\s]*$/.test(filtered)) {
23123            return eval('(' + src + ')');
23124        } else {
23125            throw new SyntaxError('Error parsing JSON, source is not valid.');
23126        }
23127    };
23128
23129    /**
23130     * jQuery.quoteString
23131     * Returns a string-repr of a string, escaping quotes intelligently.
23132     * Mostly a support function for toJSON.
23133     * Examples:
23134     * >>> jQuery.quoteString('apple')
23135     * "apple"
23136     *
23137     * >>> jQuery.quoteString('"Where are we going?", she asked.')
23138     * "\"Where are we going?\", she asked."
23139     */
23140    $.quoteString = function(string) {
23141        if (string.match(escapeable)) {
23142            return '"' + string.replace(escapeable, function(a) {
23143                var c = meta[a];
23144                if (typeof c === 'string') {
23145                    return c;
23146                }
23147                c = a.charCodeAt();
23148                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
23149            }) + '"';
23150        }
23151        return '"' + string + '"';
23152    };
23153
23154})(jQuery);
23155
23156/**
23157 * jQuery JSON Plugin
23158 * version: 2.3 (2011-09-17)
23159 *
23160 * This document is licensed as free software under the terms of the
23161 * MIT License: http://www.opensource.org/licenses/mit-license.php
23162 *
23163 * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
23164 * website's http://www.json.org/json2.js, which proclaims:
23165 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
23166 * I uphold.
23167 *
23168 * It is also influenced heavily by MochiKit's serializeJSON, which is
23169 * copyrighted 2005 by Bob Ippolito.
23170 */
23171
23172(function($) {
23173
23174    var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
23175        meta = {
23176            '\b': '\\b',
23177            '\t': '\\t',
23178            '\n': '\\n',
23179            '\f': '\\f',
23180            '\r': '\\r',
23181            '"': '\\"',
23182            '\\': '\\\\'
23183        };
23184
23185    /**
23186     * jQuery.toJSON
23187     * Converts the given argument into a JSON respresentation.
23188     *
23189     * @param o {Mixed} The json-serializble *thing* to be converted
23190     *
23191     * If an object has a toJSON prototype, that will be used to get the representation.
23192     * Non-integer/string keys are skipped in the object, as are keys that point to a
23193     * function.
23194     *
23195     */
23196    $.toJSON = typeof JSON === 'object' && JSON.stringify
23197        ? JSON.stringify
23198        : function(o) {
23199
23200        if (o === null) {
23201            return 'null';
23202        }
23203
23204        var type = typeof o;
23205
23206        if (type === 'undefined') {
23207            return undefined;
23208        }
23209        if (type === 'number' || type === 'boolean') {
23210            return '' + o;
23211        }
23212        if (type === 'string') {
23213            return $.quoteString(o);
23214        }
23215        if (type === 'object') {
23216            if (typeof o.toJSON === 'function') {
23217                return $.toJSON(o.toJSON());
23218            }
23219            if (o.constructor === Date) {
23220                var month = o.getUTCMonth() + 1,
23221                    day = o.getUTCDate(),
23222                    year = o.getUTCFullYear(),
23223                    hours = o.getUTCHours(),
23224                    minutes = o.getUTCMinutes(),
23225                    seconds = o.getUTCSeconds(),
23226                    milli = o.getUTCMilliseconds();
23227
23228                if (month < 10) {
23229                    month = '0' + month;
23230                }
23231                if (day < 10) {
23232                    day = '0' + day;
23233                }
23234                if (hours < 10) {
23235                    hours = '0' + hours;
23236                }
23237                if (minutes < 10) {
23238                    minutes = '0' + minutes;
23239                }
23240                if (seconds < 10) {
23241                    seconds = '0' + seconds;
23242                }
23243                if (milli < 100) {
23244                    milli = '0' + milli;
23245                }
23246                if (milli < 10) {
23247                    milli = '0' + milli;
23248                }
23249                return '"' + year + '-' + month + '-' + day + 'T' +
23250                    hours + ':' + minutes + ':' + seconds +
23251                    '.' + milli + 'Z"';
23252            }
23253            if (o.constructor === Array) {
23254                var ret = [];
23255                for (var i = 0; i < o.length; i++) {
23256                    ret.push($.toJSON(o[i]) || 'null');
23257                }
23258                return '[' + ret.join(',') + ']';
23259            }
23260            var name,
23261                val,
23262                pairs = [];
23263            for (var k in o) {
23264                type = typeof k;
23265                if (type === 'number') {
23266                    name = '"' + k + '"';
23267                } else if (type === 'string') {
23268                    name = $.quoteString(k);
23269                } else {
23270                    // Keys must be numerical or string. Skip others
23271                    continue;
23272                }
23273                type = typeof o[k];
23274
23275                if (type === 'function' || type === 'undefined') {
23276                    // Invalid values like these return undefined
23277                    // from toJSON, however those object members
23278                    // shouldn't be included in the JSON string at all.
23279                    continue;
23280                }
23281                val = $.toJSON(o[k]);
23282                pairs.push(name + ':' + val);
23283            }
23284            return '{' + pairs.join(',') + '}';
23285        }
23286    };
23287
23288    /**
23289     * jQuery.evalJSON
23290     * Evaluates a given piece of json source.
23291     *
23292     * @param src {String}
23293     */
23294    $.evalJSON = typeof JSON === 'object' && JSON.parse
23295        ? JSON.parse
23296        : function(src) {
23297        return eval('(' + src + ')');
23298    };
23299
23300    /**
23301     * jQuery.secureEvalJSON
23302     * Evals JSON in a way that is *more* secure.
23303     *
23304     * @param src {String}
23305     */
23306    $.secureEvalJSON = typeof JSON === 'object' && JSON.parse
23307        ? JSON.parse
23308        : function(src) {
23309
23310        var filtered =
23311            src
23312            .replace(/\\["\\\/bfnrtu]/g, '@')
23313            .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
23314            .replace(/(?:^|:|,)(?:\s*\[)+/g, '');
23315
23316        if (/^[\],:{}\s]*$/.test(filtered)) {
23317            return eval('(' + src + ')');
23318        } else {
23319            throw new SyntaxError('Error parsing JSON, source is not valid.');
23320        }
23321    };
23322
23323    /**
23324     * jQuery.quoteString
23325     * Returns a string-repr of a string, escaping quotes intelligently.
23326     * Mostly a support function for toJSON.
23327     * Examples:
23328     * >>> jQuery.quoteString('apple')
23329     * "apple"
23330     *
23331     * >>> jQuery.quoteString('"Where are we going?", she asked.')
23332     * "\"Where are we going?\", she asked."
23333     */
23334    $.quoteString = function(string) {
23335        if (string.match(escapeable)) {
23336            return '"' + string.replace(escapeable, function(a) {
23337                var c = meta[a];
23338                if (typeof c === 'string') {
23339                    return c;
23340                }
23341                c = a.charCodeAt();
23342                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
23343            }) + '"';
23344        }
23345        return '"' + string + '"';
23346    };
23347
23348})(EC$ || jQuery);
23349
23350/**
23351 * 상품연동형 js - for 프론트
23352 */
23353
23354
23355(function($) {
23356
23357    var $Olnk = {
23358         iOlinkTotalPrice: 0, // 저장형 옵션의 가격
23359         iAddOptionTotalPrice: 0, // 추가 구성상품의 가격
23360         aOptionData: new Array(), // 순차적 로딩을 위한 배열
23361         iOptionAddNum: 1, // 필수값을 표시하기 위한 번호
23362         iOptionAddProductNum: 1,
23363         aOptionAddProductNum: new Array(),
23364         aOptionProductData: new Array(),
23365         aOptionProductDataListKey: new Array(),
23366         bAllSelectedOption: false,
23367
23368         getOlnkSelectedItem: function(aStockData, bButton, sDispNonePrice, iProductPrice)
23369         {
23370             var aOption = new Array();
23371             var bItemSelected = false;
23372             var bResult = true;
23373             var sOptionId = '';
23374             var iOptPrice = 0;
23375             var iPrdPrice = CAFE24.SHOP_PRICE.toShopPrice(iProductPrice);
23376
23377             // 운영방식설정 > 회원/비회원 가격표시 설정 반영
23378             if (sDispNonePrice == 'T') {
23379                 iTotalPrice = 0;
23380             } else {
23381                 var sSelector = 'select[id^="'+product_option_id+'"]';
23382                 if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === true) {
23383                     var sCompositionCode = EC$(CAFE24.SHOP_FRONT_NEW_OPTION_BIND.oOptionObject).attr('composition-code');
23384                     sSelector = 'select[id^="'+product_option_id+'_'+sCompositionCode+'"]';
23385                 }
23386                 if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation') {
23387                    var aValueNum = [];
23388                    EC$(sSelector).each(function() {
23389                        aValueNum.push(EC$(this).val());
23390                    });
23391                    sOptionId = aValueNum.join('-');
23392                    if (aStockData.hasOwnProperty(sOptionId) === false) {
23393                        sOptionId = 'default';
23394                    }
23395                    iTotalPrice = iPrdPrice + CAFE24.SHOP_PRICE.toShopPrice(aStockData[sOptionId].stock_price);
23396                 } else {
23397                     EC$(sSelector).each(function() {
23398                         var iValNo = parseInt(EC$(this).val());
23399
23400                         if (isNaN(iValNo) === true) {
23401                             return;
23402                         }
23403
23404                         iOptPrice += CAFE24.SHOP_PRICE.toShopPrice(aStockData[iValNo].option_price);
23405                         sOptionId = iValNo;
23406                     });
23407
23408                     iTotalPrice = iPrdPrice + iOptPrice;
23409                 }
23410             }
23411
23412             EC$(sSelector).each(function() {
23413
23414                 if (EC$(this).prop('required') === false && EC$(this).val() === '*') {
23415                     return true;
23416                 }
23417                 aOption.push(EC$(this).val());
23418             });
23419
23420             // 전부 선택인 옵션만 있고 선택된 옵션이 없을때
23421             if ((Olnk.bAllSelectedOption === true || bButton === true) && aOption.length === 0) {
23422                 bItemSelected = true;
23423                 sOptionId = sProductCode;
23424             } else if (ITEM.isOptionSelected(aOption) === true) {
23425                 bItemSelected = true;
23426             }
23427
23428            // 버튼으로 처리 했을때 선택이 모두 되어 있지 않다면 튕겨 내자
23429             if (bButton === true && bItemSelected === false && aOption.length > 0) {
23430                 alert(__('필수 옵션을 선택해주세요.'));
23431                 bResult = false;
23432             }
23433
23434             // 추가입력옵션 체크!!
23435             if (bButton === true && checkAddOption() === false) {
23436                 bItemSelected = false;
23437             }
23438
23439             return {'bResult': bResult, 'bItemSelected': bItemSelected, 'aOption': aOption, 'sOptionId': sOptionId, 'iTotalPrice': iTotalPrice};
23440         },
23441
23442        /**
23443         * 최종가격 표시 핸들링 - 상품상세
23444         */
23445        handleTotalPrice: function(sOptionStockData, iProductPrice, sDispNonePrice, bButton, iManualQuantity) {
23446            var aStockData = CAFE24.UTIL.parseJSON(option_stock_data);
23447            var sOptionId = '';
23448            var iTotalPrice = 0;
23449            var iCnt = 1;
23450            var sQuantity = '('+sprintf(__('%s개'), iCnt)+')';
23451            var sPrice = '';
23452
23453            // 옵션 선택 완료 되었을때 check
23454            var aOption = new Array();
23455            var aRequiredData = new Array();
23456            var sOptionText = '';
23457            var aOptionText = new Array();
23458            var bItemSelected = false, bSoldOut = false;
23459            var iTotalQuantity = 0;
23460
23461            var aItemSelectInfo = Olnk.getOlnkSelectedItem(aStockData, bButton, sDispNonePrice, iProductPrice);
23462
23463            bResult = aItemSelectInfo.bResult;
23464            bItemSelected = aItemSelectInfo.bItemSelected;
23465            aOption = aItemSelectInfo.aOption;
23466            if (aItemSelectInfo.sOptionId !== '') {
23467                sOptionId = aItemSelectInfo.sOptionId;
23468            }
23469
23470            iTotalPrice = aItemSelectInfo.iTotalPrice;
23471
23472
23473            if (bItemSelected === true) {
23474                var sOptionText = '';
23475                var iStockNumber = aStockData[sOptionId].stock_number;
23476                var bStock = aStockData[sOptionId].use_stock;
23477                var useSoldOut = aStockData[sOptionId].use_soldout;
23478                var sIsDisplay = aStockData[sOptionId].is_display;
23479                var sIsSelling = aStockData[sOptionId].is_selling;
23480                var sIsReserveStat = aStockData[sOptionId].is_reserve_stat; //예약주문R|Q당일발송
23481                if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === true) {
23482                    useSoldOut = 'F';
23483                }
23484
23485                var iBuyUnit = CAFE24.FRONT_NEW_PRODUCT_QUANTITY_VALID.getBuyUnitQuantity('base');
23486                var iProductMin = CAFE24.FRONT_NEW_PRODUCT_QUANTITY_VALID.getProductMinQuantity();
23487
23488                var iQuantity = (iBuyUnit >= iProductMin ? iBuyUnit : iProductMin);
23489
23490                if (typeof(iManualQuantity) !== 'undefined') {
23491                    iQuantity = iManualQuantity;
23492                }
23493                if (sIsSelling == 'F' || ((iStockNumber < buy_unit || iStockNumber <= 0) && ((bStock === true && useSoldOut === 'T') || sIsDisplay == 'F'))) {
23494                    bSoldOut = true;
23495                    sOptionText = ' <span class="soldOut">['+__('품절')+']</span>';
23496                }
23497
23498                if (bSoldOut === true && isNewProductSkin() === false) {
23499                    alert(__('이 상품은 현재 재고가 부족하여 판매가 잠시 중단되고 있습니다.') + '\n\n' + __('제품명') + ' : ' + product_name);
23500                    return;
23501                }
23502
23503                //( 품절 or 추가메시지)
23504                if (aReserveStockMessage['show_stock_message'] === 'T' && sIsReserveStat !== 'N') {
23505                    var sReserveStockMessage = '';
23506                    bSoldOut = false; //품절 사용 안함
23507
23508                    sReserveStockMessage = aReserveStockMessage[sIsReserveStat];
23509                    sReserveStockMessage = sReserveStockMessage.replace(aReserveStockMessage['stock_message_replace_name'], iStockNumber);
23510                    sReserveStockMessage = sReserveStockMessage.replace('[:PRODUCT_STOCK:]', iStockNumber);
23511
23512                    sOptionText = sOptionText.replace(sReserveStockMessage, '') + ' <span class="soldOut">'+sReserveStockMessage+'</span>';
23513                }
23514
23515                // 예약형 상품은 선택되어진 옵션의 합계로 수량을 체크하지 않는다.
23516                if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation') {
23517                    iTotalQuantity = 0;
23518                } else {
23519                    // 옵션 선택시 재고 수량이 현재 선택되어진 수량보다 적을 경우 alert처리후에 return합니다.
23520                    EC$('.option_box_id').each(function(i) {
23521                        sQuantitySelector = '#' + EC$(this).attr('id').replace('id','quantity');
23522                        if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === true) {
23523                            var sCompositionCode = EC$(this).attr('composition-code');
23524                            sQuantitySelector = '#quantity_'+sCompositionCode;
23525                        }
23526                        iTotalQuantity += parseInt(EC$(sQuantitySelector).val());
23527                    });
23528                }
23529
23530
23531                if (iTotalQuantity > 0) {
23532                    iTotalQuantity += iQuantity;
23533                    if (((iStockNumber < iTotalQuantity || iStockNumber <= 0) && ((bStock === true && useSoldOut === 'T') || sIsDisplay == 'F'))) {
23534                        alert(__('재고 수량이 부족하여 더 이상 옵션을 추가하실 수 없습니다.'));
23535                        return;
23536                    }
23537                }
23538
23539                var sSelector = 'select[id^="'+product_option_id+'"]';
23540                if (typeof CAFE24.SHOP_FRONT_PRODUCT_FUNDING === 'object' && CAFE24.SHOP_FRONT_PRODUCT_FUNDING.isFundingProduct() === true) {
23541                    var sCompositionCode = EC$(CAFE24.SHOP_FRONT_NEW_OPTION_BIND.oOptionObject).attr('composition-code');
23542                    if (CAFE24.SHOP_FRONT_NEW_OPTION_BIND.oOptionObject === null) {
23543                        sCompositionCode = CAFE24.SHOP_FRONT_PRODUCT_FUNDING.sCurrentCompositionCode;
23544                    }
23545                    sSelector = 'select[id^="'+product_option_id+'_'+sCompositionCode+'"]';
23546                }
23547
23548                sOptionId = '';
23549                var aOptionTitle = [];
23550                if ((Olnk.bAllSelectedOption === true || bButton === true) && aOption.length === 0) {
23551                    EC$(sSelector).each(function() {
23552                        sSelectedOptionId = EC$(this).attr('id');
23553                        sOptionId += EC$(this).val() + '_'+EC$(this).attr('option_code') +'||';
23554                    });
23555                    aOptionText.push(__('선택한 옵션 없음'));
23556                } else {
23557                    EC$(sSelector).each(function() {
23558                        if (EC$(this).prop('required') === false && EC$(this).val() === '*') {
23559                            return true;
23560                        }
23561                        if (Olnk.getCheckValue(EC$(this).val(),'') === true) {
23562                            sSelectedOptionId = EC$(this).attr('id');
23563                            var sOptionText = EC$('#'+sSelectedOptionId+ ' option:selected').text();
23564                            if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation' && EC$(this).attr('option_title') === 'timestamp') {
23565                                var sDateTimeFormat = sOptionText.includes('T') ? EC_GLOBAL_DATETIME.const.FULL_TIME : EC_GLOBAL_DATETIME.const.DATE_ONLY;
23566                                var sOptionText = EC_GLOBAL_DATETIME.parse('20'+sOptionText, 'shop').format(sDateTimeFormat);
23567                            }
23568                            aOptionText.push(sOptionText);
23569
23570                            aOptionTitle.push({ key: EC$(this).attr('option_title'), value: EC$('#'+sSelectedOptionId+ ' option:selected').text()});
23571                        }
23572                        sOptionId += EC$(this).val() + '_'+EC$(this).attr('option_code') +'||';
23573                    });
23574                }
23575                if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation') {
23576                    var aValueNum = [];
23577                    EC$(sSelector).each(function() {
23578                        aValueNum.push(EC$(this).val());
23579                    });
23580                    sOptionId = aValueNum.join('-');
23581                    TotalAddSale.setParam('product_service_type', 'reservation');
23582                }
23583
23584                iProductPrice = getProductPrice(iQuantity, iTotalPrice, sOptionId, bSoldOut, function(iProductPrice) {
23585                    let sOptionPriceText = '';
23586                    if (isNewProductSkin() === false) {
23587                        if (sIsDisplayNonmemberPrice == 'T') {
23588                            EC$('#span_product_price_text').html(sNonmemberPrice);
23589                        } else {
23590                            EC$('#span_product_price_text').html(CAFE24.SHOP_PRICE_FORMAT.toShopPrice(iProductPrice));
23591                        }
23592                    } else {
23593                        if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation') {
23594                            let iOptionAddPrice = TotalAddSale.getReservationLinkAddPrice(sOptionId);
23595                            if (iOptionAddPrice !== 0) {
23596                                // 추가금액 표기: getOptionPrice로 부호/통화기호 ìˆ
23596œì„œë¥¼ 다른 옵션 타입과 통일
23597                                sOptionPriceText = '(' + getOptionPrice(iOptionAddPrice) + ')';
23598                            }
23599                        }
23600                        setOptionBox(sOptionId, (aOptionText.join('/')) + ' ' + sOptionText , iProductPrice, bSoldOut, sSelectedOptionId, sIsReserveStat, iManualQuantity, aOptionTitle, '', sOptionPriceText);
23601                    }
23602
23603                });
23604
23605
23606            }
23607
23608        },
23609        getOlinkOptionKey: function()
23610        {
23611            var sOptionId = '';
23612            EC$('select[id^="' + product_option_id + '"]').each(function() {
23613                if (EC$(this).prop('required') === false && EC$(this).val() === '*') {
23614                    return true;
23615                }
23616                sOptionId += EC$(this).val() + '_'+EC$(this).attr('option_code') +'||';
23617            });
23618            return sOptionId;
23619        },
23620
23621        /**
23622         * 장바구니 담기시 필요한 파라미터 생성
23623         */
23624        getSelectedItemForBasket: function(sProductCode, oTargets, iQuantity) {
23625            var options = {};
23626            var aOptionData ,aOptionTmp;
23627            var bCheckNum = false;
23628            oTargets.each(function() {
23629                aOptionData = {};
23630
23631                if (EC$(this).val().indexOf('||') >= 0) {
23632                    aOptionTmp = EC$(this).val().split('||');
23633                    for (i = 0; i < aOptionTmp.length; i++) {
23634                        if (aOptionTmp[i] !== '') {
23635                            aOptionData = aOptionTmp[i].split('_');
23636                        }
23637
23638                        if (Olnk.getCheckValue(aOptionData[0],'') === true) {
23639                            options[aOptionData[1]] = aOptionData[0];
23640                            bCheckNum = true;
23641                        }
23642                    }
23643                } else if (typeof CAFE24.FRONT_JS_CONFIG_SHOP === 'object' && CAFE24.FRONT_JS_CONFIG_SHOP.hasOwnProperty('product_service_type') && CAFE24.FRONT_JS_CONFIG_SHOP.product_service_type === 'reservation') {
23644                    aOptionTemp = EC$(this).val().split('-');
23645                    for (i = 0; i < aOptionTemp.length; i++) {
23646                        if (Olnk.getCheckValue(aOptionTemp[i],'') === true) {
23647                            options[i] = aOptionTemp[i];
23648                            bCheckNum = true;
23649                        }
23650                    }
23651                } else {
23652                    var optCode = EC$(this).attr('option_code');
23653                    var optValNo = parseInt(EC$(this).val());
23654
23655                    if (optCode == '' || optCode == null) {
23656                        return null;
23657                    }
23658                    if (isNaN(optValNo) === true) {
23659                        optValNo = '';
23660                    }
23661
23662                    if (optValNo !== '') {
23663                        options[optCode] = optValNo;
23664                        bCheckNum = true;
23665                    }
23666
23667                }
23668            });
23669
23670
23671            return {
23672                'product_code': sProductCode,
23673                'quantity': iQuantity,
23674                'options': options,
23675                'bCheckNum': bCheckNum
23676            };
23677        },
23678
23679        /**
23680         * 관심상품 담기시 필요한 파라미터 생성
23681         */
23682        getSelectedItemForWish: function(sProductCode, oTargets) {
23683            var options = {};
23684            var bCheckNum = false;
23685
23686            var aOptionData ,aOptionTmp;
23687            EC$(oTargets).each(function() {
23688
23689                aOptionTmp = EC$(this).val().split('||');
23690                aOptionData = {};
23691                options = {};
23692
23693                for (i = 0; i < aOptionTmp.length; i++) {
23694                    if (aOptionTmp[i] !== '') {
23695                        aOptionData = aOptionTmp[i].split('_');
23696                    }
23697                    //if (/^\*+$/.test(aOptionData[0]) === false) {
23698                    if (Olnk.getCheckValue(aOptionData[0],'') === true) {
23699                        options[aOptionData[1]] = aOptionData[0];
23700                        bCheckNum = true;
23701                    }
23702                }
23703            });
23704
23705            return {
23706                'product_code': sProductCode,
23707                'options': options,
23708                'bCheckNum': bCheckNum
23709            };
23710        },
23711
23712        /**
23713         * 선택된 품목정보 반환
23714         * 상품연동형에서는 item_code 가 선택한 옵션을 뜻하지 않으므로
23715         * 호환성을 위한 모조 값만 할당해준다.
23716         */
23717        getMockItemInfo: function(aInfo) {
23718            var aResult = {
23719                'product_no': aInfo.product_no,
23720                'item_code': aInfo.product_code + '000A',
23721                'opt_id': '000A',
23722                'opt_str': ''
23723            };
23724
23725            return aResult;
23726        },
23727
23728        /**
23729         * 상품연동형 옵션인지 여부 반환
23730         */
23731        isLinkageType: function(sOptionType) {
23732            if (typeof sOptionType === 'string' && sOptionType == 'E') {
23733                return true;
23734            }
23735
23736            return false;
23737        },
23738
23739        /**
23740         * 상품상세(NewProductAction) 관련 js 스크립트를 보면, create_layer 라는 함수가 있다.
23741         * 해당 함수는 ajax 콜을 해서 레이어 팝업으로 띄울 소스코드를 불러오게 되는데, 이때 스크립트 태그도 같이 따라온다.
23742         * 해당 스크립트 태그에서 불러오는 js 파일내부에는 동일한 jquery 코드가 다시한번 오버라이딩이 되는데
23743         * 이렇게 되면 기존에 물려있던 extension 메소드들은 초기화되어 날아가게 된다.
23744         *
23745         * 레이어 팝업이 뜨고 나서, $ 내에 존재해야할 메소드나 멤버변수들이 사라졌다면 이와 같은 현상때문이다.
23746         * 가장 이상적인 처리는 스크립트 태그를 없애는게 가장 좋으나 호출되는 스크립트에 의존적인 코드가 존재하는것으로 보인다.
23747         * 해당영역이 완전히 파악되기 전까진 필요한 부분에서만 예외적으로 동작할 수 있도록 한다.
23748         */
23749        bugfixCreateLayerForWish: function() {
23750            var __nil = jQuery.noConflict(true);
23751        },
23752
23753        /**
23754         * 장바구니 담기시 필요한 파라미터를 일부 조작
23755         */
23756        hookParamForBasket: function(aParam, aInfo) {
23757            if (aInfo.option_type != 'E') {
23758                return aParam;
23759            }
23760
23761            var aItemCode = this.getSelectedItemForBasket(aInfo.product_code, aInfo.targets, aInfo.quantity);
23762
23763            aParam['item_code_before'] = '';
23764            aParam['option_type'] = 'E';
23765            aParam['selected_item_by_etype[]'] = EC$.toJSON(aItemCode);
23766
23767            return aParam;
23768        },
23769
23770        /**
23771         * 관심상품 담기시 필요한 파라미터를 일부 조작
23772         */
23773        hookParamForWish: function(aParam, aInfo) {
23774            if (aInfo.option_type != 'E') {
23775                return aParam;
23776            }
23777
23778            var aItemCode = {};
23779
23780            //
23781            // aInfo.targets 는 구스킨을 사용했을 때 출력되는 옵션 셀렉트 박스의 엘리먼트 객체인데,
23782            // 현재 뉴스킨과 구스킨 구분을 아이디값이 wishlist_option_modify_layer_ 에 해당되는 노드가
23783            // 있는지로 판별하기 때문에 모호함이 존재한다.
23784            // 즉, 뉴스킨을 사용해도 해당 노드가 존재하지 않는 조건이 발생할 수 있기 때문이다.
23785            // 예를 들면, 관심상품상에 담긴 리스트가 모두 옵션이 없는 상품만 있는 경우이거나 아니면
23786            // 옵션이 존재하지만 아무것도 선택되지 않은 상품인 경우 발견이 되지 않을 수 있다.
23787            // 그러므로 이런 경우엔 셀렉트박스를 통해 선택된 옵션을 파악하는 것이 아니라,
23788            // 현재 할당되어 있는 데이터를 기준으로 파라미터를 세팅하도록 한다.
23789            //
23790            if (aInfo.targets.length > 0) {
23791                aItemCode = this.getSelectedItemForBasket(aInfo.product_code, aInfo.targets, aInfo.quantity);
23792            } else {
23793                aItemCode = aInfo.selected_item_by_etype;
23794            }
23795
23796            aParam.push('option_type=E');
23797            aParam.push('selected_item_by_etype[]=' + EC$.toJSON(aItemCode));
23798
23799            return aParam;
23800        },
23801        /**
23802         * 장바구니 담기시 필요한 파라미터 생성 - 구스킨 전용 뉴스킨 사용안함.
23803         */
23804        getSelectedItemForBasketOldSkin: function(sProductCode, oTargets, iQuantity) {
23805            var options = {};
23806            var optCode = '';
23807            var optValNo = '';
23808            var bCheckNum = false;
23809            oTargets.each(function() {
23810                optCode = EC$(this).attr('option_code');
23811                optValNo = parseInt(EC$(this).val());
23812
23813                if (optCode == '' || optCode == null) {
23814                    return null;
23815                }
23816
23817                if (isNaN(optValNo) === false) {
23818                    options[optCode] = EC$(this).val();
23819                    bCheckNum = true;
23820                }
23821            });
23822
23823            return {
23824                'product_code': sProductCode,
23825                'quantity': iQuantity,
23826                'options': options,
23827                'bCheckNum': bCheckNum
23828            };
23829        },
23830        /**
23831         * 관심상품 담기시 필요한 파라미터 생성
23832         */
23833        getSelectedItemForWishOldSkin: function(sProductCode, oTargets) {
23834            var options = {};
23835            var isReturn = true;
23836            var bCheckNum = false;
23837            oTargets.each(function() {
23838                if (isReturn === false) {
23839                    isReturn = false;
23840                    return;
23841                }
23842
23843                var optCode = EC$(this).attr('option_code');
23844                var optValNo = parseInt(EC$(this).val());
23845
23846                //
23847                // 필수입력값 체크
23848                //
23849                if (EC$(this).prop('required') === true) {
23850                    if (isNaN(optValNo) === true) {
23851                        isReturn = false;
23852                        return false;
23853                    }
23854                }
23855
23856                if (optCode == '' || optCode == null) {
23857                    isReturn = false;
23858                    return;
23859                }
23860
23861                if (isNaN(optValNo) === false) {
23862                    options[optCode] = optValNo;
23863                    bCheckNum = true;
23864                }
23865            });
23866
23867            if (isReturn === true) {
23868                return {
23869                    'product_code': sProductCode,
23870                    'options': options,
23871                    'bCheckNum': bCheckNum
23872                };
23873            }
23874
23875            return false;
23876        },
23877
23878        /*
23879         * 상단 옵션 선택후 alert후 옵션 재세팅 ( 상위 옵션이 재 세팅되면 해당 옵션에 하단 옵션들은 reset)
23880         */
23881        getOptionCheckData: function(oTarget) {
23882            //if ((/^\*+$/.test(oTarget.val()) === true && Boolean(oTarget.prop('required')) === true) || oTarget.attr('id') === undefined) {
23883            return !((Olnk.getCheckValue(oTarget.val(), '') === false && oTarget.prop('required') === true) || oTarget.attr('id') === undefined);
23884
23885
23886        },
23887        /**
23888         * 재고 체크 ( 구스킨에서 action시에 필요함.
23889         * 각각의 수량을 전부 합치고 그 합친 수량과 재고 체크
23890         * @param sOptionId 옵션 id
23891         * @returns 품절여부
23892         */
23893        getStockValidate: function (sOptionId , iQuantity) {
23894            var aStockData = CAFE24.UTIL.parseJSON(option_stock_data);
23895            var bSoldOut = false;
23896            var iStockNumber , bStock , bStockSoldOut;
23897            // get_stock_info
23898            if (aStockData[sOptionId] == undefined) {
23899                iStockNumber = -1;
23900                bStock = false;
23901                bStockSoldOut = 'F';
23902            } else {
23903                iStockNumber = aStockData[sOptionId].stock_number;
23904                bStock = aStockData[sOptionId].use_stock;
23905                bStockSoldOut = aStockData[sOptionId].use_soldout;
23906
23907            }
23908            if (bStockSoldOut == 'T' && bStock === true && (iStockNumber < iQuantity)) {
23909                bSoldOut = true;
23910            }
23911            return bSoldOut;
23912        },
23913        /*
23914         * check value
23915         */
23916        getCheckValue: function (oTargetValue , oTarget) {
23917            if (/^\*+$/.test(oTargetValue) === true) {
23918                if (oTarget !== '') {
23919                    oTarget.val('*');
23920                }
23921                return false;
23922            }
23923            return true;
23924        },
23925        /*
23926         * 추가 구성상품의 재고 체크
23927         * @param aOptionBoxInfo 추가 구성상품 데이터
23928         */
23929        getAddProductStock: function (aOptionBoxInfo) {
23930            var iTotalQuantity = aOptionBoxInfo['iTotalQuantity'];
23931            if (this.isLinkageType(aOptionBoxInfo['option_type']) === true) {
23932                EC$('.option_add_box_'+aOptionBoxInfo['product_no']).each(function() {
23933                    // 수량 증가시 본인꺼는 빼야 한다..
23934                    if (aOptionBoxInfo['sOptionBoxId'] !== EC$(this).attr('id')) {
23935                        iTotalQuantity += parseInt(iQuantity = EC$('#' + EC$(this).attr('id').replace('id','quantity')).val());
23936                    }
23937
23938                });
23939                if (aOptionBoxInfo['is_stock'] === true && aOptionBoxInfo['use_soldout'] === true && aOptionBoxInfo['stock_number'] < iTotalQuantity) {
23940                    alert(sprintf(__('%s의 재고가 부족합니다.'), aOptionBoxInfo['title']));
23941                    //alert(aOptionBoxInfo['title'] + ' - ' + __('의 재고가 부족합니다.'));
23942                    return false;
23943                }
23944            }
23945        },
23946        /*
23947         * 모든 상품의 옵션이 선택일때 옵션박스가 떨궈지지 않았을 경우 (아무것도 선택안하면 option_box 안생김)
23948         * @param aOptionBoxInfo 추가 구성상품 데이터
23949         */
23950        getProductAllSelected: function (sProductCode, oTargets, iQuantity) {
23951            var bAllSelected = true;
23952            var options = {};
23953            oTargets.each(function(i) {
23954                if (EC$(this).val().indexOf('||') >= 0) {
23955                    aOptionTmp = EC$(this).val().split('||');
23956                    for (i = 0; i < aOptionTmp.length; i++) {
23957                        if (aOptionTmp[i] !== '') {
23958                            aOptionData = aOptionTmp[i].split('_');
23959                        }
23960                        options[aOptionData[1]] = '';
23961                    }
23962                } else {
23963                    if (EC$(this).prop('required') === true || Olnk.getCheckValue(EC$(this).val() , '') === true) {
23964                        bAllSelected = false;
23965                        return false;
23966                    }
23967                    var optCode = EC$(this).attr('option_code');
23968                    var optValNo = parseInt(EC$(this).val());
23969
23970                    if (optCode == '' || optCode == null) {
23971                        return null;
23972                    }
23973                    if (isNaN(optValNo) === true) {
23974                        optValNo = '';
23975                    }
23976                    options[optCode] = optValNo;
23977                }
23978            });
23979
23980            if (bAllSelected === true) {
23981                return {
23982                    'product_code': sProductCode,
23983                    'quantity': iQuantity,
23984                    'options': options
23985                };
23986            } else {
23987                return false;
23988            }
23989
23990        },
23991
23992        /*
23993         * 옵션 추가버튼 ( 신규 스킨의 연동형 옵션일때 품목 추가 버튼 생김)
23994         * totalProducts가 있을때 신규 스킨
23995         * ( NewProductOption.js에 isNewProductSkin이 있지만 의존적 처리가 어려움)
23996         * oPushButton 품목 추가 버튼 Object
23997         */
23998        getOptionPushbutton: function(oPushButton) {
23999            if (typeof(option_push_button) !== 'undefined' && option_push_button === 'T' && oPushButton.length > 0 && isNewProductSkin() === true) {
24000                return true;
24001            } else {
24002                return false;
24003            }
24004
24005        },
24006
24007        /*
24008         * 옵션 추가버튼 action. php 에서 assign된 함수
24009         */
24010        setOptionPushButton: function() {
24011            Olnk.handleTotalPrice(option_stock_data, product_price, sIsDisplayNonmemberPrice , true);
24012        },
24013        /**
24014         * 옵션 추가 버튼 연동형 옵션인 경우에만 동작 하자.(이건 추가구성상품)
24015         * @param iProductNum 상품번호
24016         */
24017        setAddOptionPushButton: function(iProductNum) {
24018            ProductAdd.setAddProductOptionPushButton(iProductNum);
24019        },
24020        setSetOptionPushButton: function(iProductNum) {
24021            ProductSet.setSetProductOptionPushButton(iProductNum);
24022        },
24023
24024        /**
24025         * custom_data에 필요한 품주코드
24026         * @param {string} sProductCode 상품코드
24027         * @param {int} iTotalOptionCount 선택된 품목 개수
24028         * @param {int} iCurrentIndex 현재 인덱스
24029         * @returns {string}
24030         */
24031        getCustomOptionItemCode: function (sProductCode, iTotalOptionCount, iCurrentIndex) {
24032            return sProductCode + '000A_' + (iTotalOptionCount - iCurrentIndex);
24033        }
24034    };
24035
24036    //
24037    // 공개 인터페이스
24038    //
24039    window['Olnk'] = $Olnk;
24040
24041})($);
24042
24043
24044/**
24045 * 품절품목일 경우 품절 문구에대한 처리
24046 */
24047CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_SOLDOUT = {
24048    /**
24049     * 품절문구 설정 정보
24050     */
24051    aSoldoutText: null,
24052
24053    /**
24054     * 필수 메서드
24055     * @param iProductNum 상품번호
24056     * @param sItemCode 품목코드
24057     * @returns 설정에따라 품절문구 리턴
24058     * @final
24059     */
24060    get: function(iProductNum, sItemCode) {
24061        return this.getStockText(iProductNum, sItemCode);
24062    },
24063
24064    /**
24065     * 품절문구 설정(기존로직 그대로
24066     * @param iProductNum 상품번호
24067     * @returns 해당 상품의 품절 설정 문구
24068     */
24069    getSoldoutDiplayText: function (iProductNum) {
24070        if (typeof aSoldoutDisplay === 'undefined') {
24071            return CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_SOLDOUT_DEFAULT_TEXT;
24072        }
24073
24074        if (this.aSoldoutText === null) {
24075            if (typeof aSoldoutDisplay === 'string') {
24076                this.aSoldoutText = CAFE24.UTIL.parseJSON(aSoldoutDisplay);
24077            } else {
24078                this.aSoldoutText = aSoldoutDisplay;
24079            }
24080        }
24081
24082        if (typeof this.aSoldoutText[iProductNum] === 'undefined') {
24083            this.aSoldoutText[iProductNum] = CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_SOLDOUT_DEFAULT_TEXT;
24084        }
24085
24086        return this.aSoldoutText[iProductNum];
24087    },
24088
24089    /**
24090     * 해당 품목이 품목일 경우 표시될 품절표시 Text
24091     * @param iProductNum 상품번호
24092     * @param sItemCode 아이템코드
24093     * @returns 표시될 품절문구
24094     */
24095    getStockText: function(iProductNum, sItemCode) {
24096        var sSoldoutText = '';
24097        var bIsSoldout = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isSoldout(iProductNum, sItemCode);
24098
24099        if (bIsSoldout === true) {
24100            sSoldoutText = ' [' + this.getSoldoutDiplayText(iProductNum) + ']';
24101        }
24102
24103        if (typeof(aReserveStockMessage) === 'undefined') {
24104            return sSoldoutText;
24105        }
24106
24107        var aStockData = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getProductStockData(iProductNum);
24108
24109        if (typeof(aStockData[sItemCode]) === 'undefined') {
24110            return sSoldoutText;
24111        }
24112
24113        if (aStockData[sItemCode].is_reserve_stat !== 'N') {
24114            sSoldoutText = aReserveStockMessage[aStockData[sItemCode].is_reserve_stat];
24115            sSoldoutText = sSoldoutText.replace(aReserveStockMessage['stock_message_replace_name'], aStockData[sItemCode].stock_number);
24116            sSoldoutText = sSoldoutText.replace('[:PRODUCT_STOCK:]', aStockData[sItemCode].stock_number);
24117        }
24118
24119        return sSoldoutText;
24120    }
24121};
24122
24123var EC_SHOP_FRONT_NEW_OPTION_EXTRA_SOLDOUT = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_SOLDOUT');
24124
24125/**
24126 * 해당 품목에 대한 추가금액 표시여부
24127 */
24128CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_PRICE = {
24129    /**
24130     * 추가금액 표시여부 설정
24131     */
24132    aOptionPriceDisplayConf: [],
24133
24134    oChooseObject: null,
24135
24136    /**
24137     * 필수 메서드
24138     * @param iProductNum 상품번호
24139     * @param sItemCode 품목코드
24140     * @param eChooseObject 현재 선택한 옵션 Object
24141     * @returns 설정에따라 표시할 경우 품목의 추가금액 리턴
24142     * @final
24143     */
24144    get: function(iProductNum, sItemCode, eChooseObject) {
24145        this.oChooseObject = eChooseObject;
24146        return this.getAddPriceText(iProductNum, sItemCode);
24147    },
24148
24149    /**
24150     * 각 옵션선택시마다 실행되는 가격관련 메서드
24151     * @param oOptionChoose 구분할 옵션박스 object
24152     * @returns bool
24153     */
24154    eachCallback: function(oOptionChoose) {
24155        //구스킨에서 옵션선택시마다 표시항목 판매가부분의 가격에 옵션추가듬액 계산
24156        this.setDisplayProductPriceForOldSkin(oOptionChoose);
24157    },
24158
24159    /**
24160     * 구스킨에서 옵션선택시마다 표시항목 판매가부분의 가격에 옵션추가듬액 계산
24161     * @param oOptionChoose 구분할 옵션박스 object
24162     */
24163    setDisplayProductPriceForOldSkin: function(oOptionChoose) {
24164        //뉴스킨이라면 패스 (ECHOSTING-241102 모바일 관심상품리스트 오류)
24165        if (EC$('#totalProducts').length > 0) {
24166            return;
24167        }
24168
24169        //해당 function이 존재할때만 실행
24170        if (typeof(setOldTotalPrice) !== 'function') {
24171            return;
24172        }
24173
24174        var sID = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionChooseID(oOptionChoose);
24175
24176        //상품상세 메인상품에 대해서만 실행
24177        if (/^product_option_id+/.test(sID) !== true) {
24178            return;
24179        }
24180
24181        //구스킨일 경우 각 옵션선택시마다 실행
24182        try {
24183            setOldTotalPrice();
24184        } catch (e) {
24185            CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(oOptionCh
24185oose, '*');
24186        }
24187    },
24188
24189    /**
24190     * 옵션 추가금액에대한 Display텍스트
24191     * @param iProductNum 상품번호
24192     * @param sItemCode 품목코드
24193     * @returns string 옵션 추가금액 Text
24194     */
24195    getAddPriceText: function(iProductNum, sItemCode) {
24196        //추가금액 표시여부
24197        var bIsDisplayOptionPrice = this.getOptionPriceDisplay(iProductNum);
24198
24199        if (bIsDisplayOptionPrice === false) {
24200            return '';
24201        }
24202
24203        var iAddPrice = this.getAddPrice(iProductNum, sItemCode);
24204
24205        if (iAddPrice !== false) {
24206            var sPrefix = '';
24207            if (iAddPrice > 0.00) {
24208                sPrefix = '+';
24209            } else {
24210                sPrefix = '-';
24211            }
24212
24213            //화폐단위가 +- 기호 뒤에와야해서 여기서 양수로 바꿈
24214            iAddPrice = Math.abs(iAddPrice);
24215            // [ECQAINT-79686] presentment 활성 시 결제화폐 환산+통화기호 (비활성 시 헬퍼 내부에서 기존 포맷 폴백)
24216            var sStr = ' (' + sPrefix + (SHOP.isG2G() === true ? CAFE24.SHOP_PRICE.toShopPricePresentment(iAddPrice) : CAFE24.SHOP_PRICE_FORMAT.toShopPricePresentment(iAddPrice)) +')';
24217            //그냥 값을 더할경우 원표시(\)가 &#8361;로 변환되어서 clone으로 다시가져오게 처리
24218            return EC$('<div>').append(sStr).html();
24219        }
24220
24221        return '';
24222    },
24223
24224    /**
24225     * 해당 품목의 추가금액을 가져온다(없을 경우에는 false를 리턴
24226     * @param iProductNum 상품번호
24227     * @param sItemCode 품목코드
24228     * @returns int|boolean 추가금액
24229     */
24230    getAddPrice: function(iProductNum, sItemCode)
24231    {
24232        var aStockData = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getProductStockData(iProductNum);
24233        if (typeof(aStockData[sItemCode].stock_price) !== 'undefined' && parseFloat(aStockData[sItemCode].stock_price) !== 0.00) {
24234            return parseFloat(aStockData[sItemCode].stock_price);
24235        }
24236
24237        return false;
24238    },
24239
24240    /**
24241     * 옵션 추가금액 표시여부 설정
24242     * @param iProductNum 상품번호
24243     * @returns 표시여부
24244     */
24245    getOptionPriceDisplay: function(iProductNum) {
24246        if (typeof CAFE24.SHOP_FRONT_NEW_OPTION_DATA.aOptionPriceDisplayConf[iProductNum] === 'undefined') {
24247            return 'T';
24248        }
24249
24250        return (CAFE24.SHOP_FRONT_NEW_OPTION_DATA.aOptionPriceDisplayConf[iProductNum] === 'T');
24251    }
24252};
24253
24254var EC_SHOP_FRONT_NEW_OPTION_EXTRA_PRICE = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_PRICE');
24255
24256/**
24257 * 옵션 선택 또는 품목선택완료시 상세이미지 변경
24258 */
24259CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE = {
24260    /**
24261     * 모바일과 상세이미지 클래스가 틀려서
24262     */
24263    sDetailImageClass: '',
24264
24265    /**
24266     * 세트상품의 이미지 영역
24267     */
24268    sSetProductImageID: '',
24269
24270    /**
24271     * 스와이프기능을 사용하는 상품상세인지 확인(모바일전용)
24272     */
24273    isSwipe: false,
24274
24275    /**
24276     * 세트상품인지 여부
24277     */
24278    bIsSetProduct: false,
24279
24280    /**
24281     * 각 옵션선택시마다 이미지가 있다면 상세이미지에 반영되도록 함
24282     * @param oOptionChoose 구분할 옵션박스 object
24283     */
24284    eachCallback: function(oOptionChoose) {
24285        this.bIsSetProduct = false;
24286
24287        //세트상품일 경우에 대한 처리
24288        if (/^setproduct_option_+/.test(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectGroup(oOptionChoose)) === true) {
24289            this.bIsSetProduct = true;
24290            this.sSetProductImageID = '#ec-set-product-composed-product-';
24291        }
24292
24293        if (/^addproduct_option_+/.test(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectGroup(oOptionChoose)) === true) {
24294            this.bIsSetProduct = true;
24295            this.sSetProductImageID = '#ec-add-product-composed-product-';
24296        }
24297
24298        if (this.isDisplayImage(oOptionChoose) === false) {
24299            return;
24300        }
24301
24302        var oSelectedOption = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectedElement(oOptionChoose);
24303
24304        if (typeof(oSelectedOption.attr('link_image')) === 'undefined' || oSelectedOption.attr('link_image').length < 1) {
24305            return;
24306        }
24307
24308        this.setImage(oSelectedOption.attr('link_image'), true, oOptionChoose);
24309    },
24310
24311    /**
24312     * 옵션 전체 선택완료후 해당 옵션품목에 연결된 이미지를 상세이미지에 반영되도록 함
24313     * @param oOptionChoose 구분할 옵션박스 object
24314     */
24315    completeCallback: function(oOptionChoose) {
24316        //연동형은 제외
24317        if (Olnk.isLinkageType(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionType(oOptionChoose)) === true) {
24318            return;
24319        }
24320
24321        if (this.isDisplayImage(oOptionChoose) === false) {
24322            return;
24323        }
24324
24325        var sItemCode = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getItemCode(oOptionChoose);
24326
24327        if (sItemCode === false) {
24328            return;
24329        }
24330
24331        var iProductNo = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oOptionChoose);
24332
24333        var aStockData = CAFE24.SHOP_FRONT_NEW_OPTION_DATA.getProductStockData(iProductNo);
24334
24335        if (typeof(aStockData[sItemCode].item_image_file) !== 'undefined' && CAFE24.UTIL.trim(aStockData[sItemCode].item_image_file) !== '') {
24336            this.setImage(aStockData[sItemCode].item_image_file, false, oOptionChoose);
24337        }
24338    },
24339
24340    /**
24341     * 이미지 출력이 가능한지 확인
24342     * @param oOptionChoose 구분할 옵션박스 object
24343     * @returns bool
24344     */
24345    isDisplayImage: function(oOptionChoose) {
24346        //세트상품일 경우에 대한 처리
24347        if (this.bIsSetProduct === true) {
24348            return this.isDisplayImageDesignForSetProduct(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oOptionChoose));
24349        } else {
24350            //추가구성상품등은 모두 제외하고 상품상세의 대표상품만 변경
24351            return this.isDisplayImageDesign();
24352        }
24353    },
24354
24355    /**
24356     * 세트상품에서 구성상품의 옵션선택시 이미비 변경 가능여부
24357     * @param iProductNum 상품번호
24358     * @returns {boolean}
24359     */
24360    isDisplayImageDesignForSetProduct: function(iProductNum)
24361    {
24362        var oSetProductImageElement = EC$(this.sSetProductImageID + iProductNum);
24363
24364        //해당 구성상품의 이미지영역이 없거나 id가 지정되지 않았으면 false
24365        if (oSetProductImageElement.length < 1) {
24366            return false;
24367        }
24368
24369        return true;
24370    },
24371
24372    /**
24373     * 디자인에서 이미지가 노출될수있는 디자인인지 확인
24374     * 상품상세에서 동일하게 사용하기위해서 따로 메서드로 분리
24375     * @returns {boolean}
24376     */
24377    isDisplayImageDesign: function()
24378    {
24379        var isMobile = false;
24380        if (typeof(mobileWeb) !== 'undefined' && mobileWeb === true) {
24381            isMobile = true;
24382            this.sDetailImageClass = '.bigImage';
24383        } else {
24384            this.sDetailImageClass = '.BigImage';
24385        }
24386
24387        if (isMobile === true) {
24388            if (EC$('.xans-product-mobileimage').length > 0) {
24389                this.isSwipe = true;
24390            }
24391        }
24392
24393        //상세이미지가 없다면 패스
24394        if (this.isSwipe === false && EC$(this.sDetailImageClass).length < 1) {
24395            return false;
24396        }
24397
24398        return true;
24399    },
24400
24401    /**
24402     * 각 디자인에 따라 옵션 도는 품목이미지를 상세이미지에 노출
24403     * @param sUrl 이미지주소
24404     * @param isOptionFlag 각 옵션의 이미지가 아닌 모든옵션 선택후 품목의 이미지이면 false
24405     * @param oOptionChoose 구분할 옵션박스 object
24406     */
24407    setImage: function(sUrl, isOptionFlag, oOptionChoose)
24408    {
24409        if (this.bIsSetProduct === true) {
24410            EC$(this.sSetProductImageID + CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oOptionChoose)).attr('src', sUrl);
24411        } else {
24412            //스와이프기능을 사용할때
24413            if (this.isSwipe === true) {
24414                if (isOptionFlag === false) {
24415                    //모든 옵션 선택후 품목에 연결이미지
24416                    this.setSwipeImage(sUrl, false);
24417                } else {
24418                    //각 업션에 대한 옵션이미지
24419                    this.setSwipeImage('', true);
24420
24421                    var iIndex = EC$('div.xans-product-mobileimage').find('li img[src="' + sUrl + '"]').parent().index();
24422                    EC$('div.typeSwipe').find('span > button').eq(iIndex).trigger('click');
24423                }
24424            } else {
24425                //스와이프기능을 사용안할때
24426                EC$(this.sDetailImageClass).attr('src', sUrl);
24427            }
24428        }
24429    },
24430
24431    /**
24432     * 모바일 상품상세에서 스와이프 사용중일때
24433     * 각 옵션의 연결이미지는 기존스와이프 영역으로
24434     * 각 품목의 연결이미지는 원래 대표이미지 영역이 나오도록 함
24435     * @param sSrc 해당 이미지 주소(품목 연결이미지일떄만)
24436     * @param bIsShowSlide true => 각 옵션별 연결이미지, false => 각 품목별 연결이미지
24437     * @param iButtonIndex 이미지 선택 버튼 순서값
24438     * @param oTarget 처리할 스와이프 모듈 Object
24439     */
24440    setSwipeImage: function(sSrc, bIsShowSlide, iButtonIndex, oTarget)
24441    {
24442        var oElement = EC$('div.xans-product-mobileimage');
24443        var oSwipe = EC$('div.typeSwipe');
24444        if (bIsShowSlide === true) {
24445            oElement.find('ul.eOptionImageCloneTemplate').remove();
24446            oElement.find('ul').show();
24447
24448            //품목이미지가 노출된후 다시 슬라이드버튼을 누르때 시간차로인대 css가 먹지 않아서 추가
24449            if (typeof(iButtonIndex) !== 'undefined') {
24450
24451                // 만약 파라미터로 받은 특정 스와이프 모듈 Object가 존재하면 해당 모듈에서만 처리하도록 추가
24452                if (typeof(oTarget) !== 'undefined') {
24453                    oSwipe = oTarget;
24454                }
24455
24456                oSwipe.find('span > button').eq(iButtonIndex).addClass('selected');
24457            }
24458        } else {
24459
24460            //첫번째 이미지를 기준으로 height가 정해지기때문에
24461            //두번째 이미지에 할당함
24462            oSwipe.find('span > button').eq(1).trigger('click');
24463            var oClone = oElement.find('ul').clone();
24464            oClone.addClass('eOptionImageCloneTemplate');
24465
24466            //추가이미지가 1개만 있을경우에는 따로 삭제하지 않음
24467            //추가이미지가 하개이면 버튼이 원래 두개이므로 따로 삭제하지 않아도 됨
24468            if (oSwipe.find('span > button').length > 2) {
24469                oClone.find('li').not('li').eq(0).not('li').eq(1).remove();
24470            }
24471            oClone.find('li').eq(1).find('img').attr('src', sSrc);
24472
24473            oSwipe.find('span > button').removeClass('selected');
24474
24475            oElement.find('ul').hide();
24476
24477            oElement.find('ul').eq(0).before(oClone);
24478        }
24479    }
24480};
24481
24482var EC_SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE');
24483
24484/**
24485 * 버튼 또는 미리보기 옵션일 경우 지정된 엘리먼트에 선택한 옵션값 보여주기
24486 */
24487CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM = {
24488    TARGET_ELEMENT_CLASS: '.ec-shop-front-product-option-desc-trigger',
24489    /**
24490     * 각 옵션선택시마다 이미지가 있다면 상세이미지에 반영되도록 함
24491     * @param oOptionChoose 구분할 옵션박스 objectboolean
24492     */
24493    eachCallback: function(oOptionChoose) {
24494        //버튼 또는 미리보기 옵션이 아니면 리턴
24495        if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(oOptionChoose) === false) {
24496            return;
24497        }
24498
24499        //셀렉터에 ""를 안붙이면 가끔 특정상횡에서 스크립트오류
24500        var oTargetOptionChoose = EC$(oOptionChoose).parent();
24501        var oTarget = oTargetOptionChoose.find("" + this.TARGET_ELEMENT_CLASS + "");
24502
24503        //디자인이 없다면 패스
24504        if (EC$(oTarget).length < 1) {
24505            if (CAFE24.checkChannelUI() === true) {
24506                // 부모창을 한번더 찾기
24507                var oTmpTarget = oTargetOptionChoose.parent().find("" + this.TARGET_ELEMENT_CLASS + "");
24508
24509                //그래도 없다면 종료
24510                if (EC$(oTmpTarget).length < 1) {
24511                    return;
24512                }
24513                oTarget = oTmpTarget;
24514            } else {
24515                return;
24516            }
24517        }
24518
24519        var sText = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectedText(oOptionChoose);
24520
24521        //선택항목에 text가 있다면
24522        //추후에 셀렉트박스가 추가된다면... *나 **가 선택되었다면 예외처리해야함
24523        if (typeof(sText) !== 'undefined' && CAFE24.UTIL.trim(sText) !== '') {
24524            EC$(oTarget).removeClass('ec-product-value').addClass('ec-product-value');
24525            EC$(oTarget).html(sText);
24526        } else {
24527            EC$(oTarget).removeClass('ec-product-value');
24528            EC$(oTarget).html(EC$(oTarget).attr('data-option_msg'));
24529        }
24530    }
24531};
24532
24533var EC_SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM');
24534
24535CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_ITEMSELECTION = {
24536    oCommon: null,
24537    initObject: function()
24538    {
24539        if (this.oCommon !== null) {
24540            return;
24541        }
24542        this.oCommon = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON;
24543    },
24544    sOptionKey: null,
24545    prefetch: function(oOptionChoose)
24546    {
24547        this.initObject();
24548
24549        if (oSingleSelection.isItemSelectionTypeM() === true) {
24550            return;
24551        }
24552
24553        // 동일한 키로 선택된 상품이 없다면 prefetch는 할일이 없음
24554        var oTarget = this.getDeleteTriggerElement(oOptionChoose);
24555        if (oTarget.length === 0) {
24556            return;
24557        }
24558
24559        if (this.oCommon.getOptionType(oOptionChoose) === 'F') {
24560            return this.prefetchOptionTypeF(oOptionChoose);
24561        }
24562
24563        if (this.oCommon.getOptionType(oOptionChoose) === 'E') {
24564            return this.prefetchOptionTypeE(oOptionChoose);
24565        }
24566
24567        oTarget.click();
24568    },
24569    prefetchOptionTypeE: function(oOptionChoose)
24570    {
24571        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24572        if (sOptionGroup.indexOf('setproduct') < 0) {
24573            this.getDeleteTriggerElement(oOptionChoose).click();
24574            return;
24575        }
24576        var oTarget = this.getDeleteTriggerElement(oOptionChoose);
24577
24578        var sOptionId = oTarget.attr('id').substring(0, oTarget.attr('id').lastIndexOf('_'));
24579        var sOptionKey = EC$('#'+sOptionId+'_id').val();
24580        this.sOptionKey = sOptionKey;
24581
24582        var sContext = this.getDeleteTriggerContext(oOptionChoose);
24583        EC$(sContext).remove();
24584
24585        this.hookIndividualSetProductParameter(sOptionKey);
24586    },
24587    prefetchOptionTypeF: function(oOptionChoose)
24588    {
24589        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24590        var oTarget = this.getDeleteTriggerElement(oOptionChoose);
24591        var sOptionId = oTarget.attr('id').substring(0, oTarget.attr('id').lastIndexOf('_'));
24592        var sOptionKey = EC$('#'+sOptionId+'_id').val();
24593        this.sOptionKey = sOptionKey;
24594
24595        // 추가구성상품
24596        if (sOptionGroup.indexOf('addproduct') > -1) {
24597            var aOptionInfo = EC$('#'+sOptionId+'_id').val().split('||');
24598            sOptionKey = aOptionInfo[0];
24599            this.sOptionKey = sOptionKey;
24600            ProductAdd.delOptionBoxData(sOptionKey);
24601        }
24602
24603        // 일반상품, 추가구성상품 동일
24604        TotalAddSale.removeProductData(sOptionKey);
24605
24606        // 세트상품
24607        if (sOptionGroup.indexOf('setproduct') > -1) {
24608            this.hookIndividualSetProductParameter(sOptionKey);
24609        }
24610    },
24611    eachCallback: function(oOptionChoose)
24612    {
24613        if (oSingleSelection.isItemSelectionTypeM() === true) {
24614            return;
24615        }
24616        var sOptionType = this.oCommon.getOptionType(oOptionChoose);
24617
24618        if (sOptionType === 'F') {
24619            return this.eachCallbackOptionTypeF(oOptionChoose);
24620        }
24621        if (sOptionType === 'E') {
24622            return this.eachCallbackOptionTypeE(oOptionChoose);
24623        }
24624    },
24625    eachCallbackOptionTypeE: function(oOptionChoose)
24626    {
24627        // 뭔가 값이 선택됐을때는 원래 돌던대로 돌린다
24628        if (this.oCommon.getOptionSelectedValue(oOptionChoose) !== '*') {
24629            return;
24630        }
24631        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24632        // 선택한 값이 취소된 경우에만 이 로직을 실행한다
24633        // 모두 선택인 경우에는 하나라도 선택되었는지
24634        if (this.oCommon.validation.checkRequiredOption(sOptionGroup) === false) {
24635            bIsSelectedRequiredOption = this.oCommon.validation.isOptionSelected(oOptionChoose);
24636        } else {
24637            bIsSelectedRequiredOption = this.oCommon.validation.isSelectedRequiredOption(sOptionGroup);
24638        }
24639        // 뭔가 하나 선택되어있는 경우
24640        if (this.oCommon.getItemCode(oOptionChoose) === false && bIsSelectedRequiredOption === true) {
24641            var oOptionGroup = this.oCommon.getOptionLastSelectedElement(sOptionGroup);
24642            if (sOptionGroup.indexOf('addproduct') > -1) {
24643                var iProductNum = this.oCommon.getOptionProductNum(oOptionChoose);
24644                if (this.oCommon.isOptionStyleButton(oOptionChoose) === true) {
24645                    ProductAdd.setAddProductOptionPushButton(iProductNum);
24646                }
24647            } else {
24648                if (typeof(ProductSet) === 'object') {
24649                    if (this.oCommon.isOptionStyleButton(oOptionGroup) === true) {
24650                        var oOptionGroup = EC$('select[product_option_area_select="' + EC$(oOptionGroup).attr('product_option_area') + '"][id="' + EC$(oOptionGroup).attr('ec-dev-id') + '"]');
24651                    }
24652                    oSingleSelection.setProductTargetKey(oOptionGroup, 'setproduct');
24653                    ProductSet.procOptionBox(oOptionGroup);
24654                } else {
24655                    if (typeof(setPrice) === 'function') {
24656                        var sID = this.oCommon.getOptionChooseID(oOptionGroup);
24657                        setPrice(false, true, sID);
24658                    }
24659                }
24660            }
24661        } else {
24662            if (sOptionGroup.indexOf('setproduct') === -1) {
24663                return;
24664            }
24665            this.hookIndividualSetProductParameter(this.sOptionKey);
24666            if (Object.keys(ProductSet.getSetIndividualList()).length > 0) {
24667                TotalAddSale.getCalculatorSalePrice(ProductSet.setTotalPrice);
24668            }
24669        }
24670    },
24671    hookIndividualSetProductParameter: function(sOptionKey)
24672    {
24673        ProductSet.delOptionBoxData(sOptionKey);
24674        // 분리세트 상품 코드 삭제
24675        var oSetIndividualList = ProductSet.getSetIndividualList();
24676        delete oSetIndividualList[sOptionKey];
24677        TotalAddSale.setParam('unit_set_product_no', oSetIndividualList);
24678
24679        // 할인 금액 품목 코드 삭제
24680        TotalAddSale.removeProductData(sOptionKey);
24681
24682        // 아무 옵션이 없는 경우
24683        if (Object.keys(oSetIndividualList).length === 0) {
24684            TotalAddSale.setParam('product', oProductList);
24685            TotalAddSale.setTotalAddSalePrice(0);
24686            ProductSet.setTotalPrice();
24687        } else {
24688            var aProductNo = [];
24689            for (var i = 0; i < Object.keys(oSetIndividualList).length; i++) {
24690                var iProductNum = oSetIndividualList[Object.keys(oSetIndividualList)[i]];
24691                if (aProductNo.indexOf(iProductNum) === -1) {
24692                    aProductNo.push(iProductNum);
24693                }
24694            }
24695            if (aProductNo.length === 1) {
24696                TotalAddSale.setParam('product_no', aProductNo[0]);
24697                TotalAddSale.setParam('is_set', false);
24698            } else {
24699                TotalAddSale.setParam('product_no', iProductNo);
24700                TotalAddSale.setParam('is_set', true);
24701            }
24702        }
24703
24704    },
24705    eachCallbackOptionTypeF: function(oOptionChoose)
24706    {
24707        if (this.oCommon.getOptionSelectedValue(oOptionChoose) === '*') {
24708            var oTarget = this.getDeleteTriggerElement(oOptionChoose);
24709            // 옵션이 실제로 취소되었음
24710            oTarget.click();
24711        } else {
24712            // 다른 옵션으로 변경되었음 - 삭제 액션이 아니라 삭제된거처럼 만들어야함
24713            var sContext = this.getDeleteTriggerContext(oOptionChoose);
24714            EC$(sContext).remove();
24715        }
24716        return true;
24717    },
24718    getDeleteTriggerElement: function(oOptionChoose)
24719    {
24720        var sSelector = this.getDeleteTriggerSelector(oOptionChoose);
24721        var sContext = this.getDeleteTriggerContext(oOptionChoose);
24722
24723        return EC$(sSelector, sContext);
24724    },
24725    getTargetKey: function(oOptionChoose)
24726    {
24727        // 기본상품(옵션없는 상품, 조합형옵션 상품, 연동형 옵션 상품, 일체형 세트상품) : 상품번호
24728        // 독립형 옵션 상품 : 상품번호|옵션순서
24729        // 분리세트 상품 : 구성상품번호|세트상품번호
24730        // 분리세트상품의 독립형 옵션 상품 : 구성상품번호|세트상품번호|옵션순서
24731        var sTargetKey = this.oCommon.getOptionProductNum(oOptionChoose);
24732        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24733        if (sOptionGroup.indexOf('setproduct') > -1) {
24734            if (sSetProductType === 'S') {
24735                sTargetKey = sTargetKey + '|' + iProductNo;
24736            } else {
24737                sTargetKey = iProductNo;
24738            }
24739        }
24740        if (this.oCommon.getOptionType(oOptionChoose) === 'F') {
24741            sTargetKey = sTargetKey + '|' + this.oCommon.getOptionSortNum(oOptionChoose);
24742        }
24743        return sTargetKey;
24744
24745    },
24746    getDeleteTriggerContext: function(oOptionChoose)
24747    {
24748        var sOptionElementType = 'tr';
24749
24750        if (CAFE24.checkChannelUI() === true) {
24751            sOptionElementType = 'div';
24752        }
24753
24754        if (typeof(ProductSet) === 'object') {
24755            sOptionElementType = ProductSet.getOptionElementType();
24756        }
24757
24758        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24759        var sContext = sOptionElementType +'.add_product';
24760        if (sOptionGroup.indexOf('addproduct') < 0) {
24761            sContext = sOptionElementType +'.option_product';
24762        }
24763        var sTargetKey = this.getTargetKey(oOptionChoose);
24764        return sContext+'[target-key="'+ sTargetKey +'"]';
24765    },
24766    getDeleteTriggerSelector: function(oOptionChoose)
24767    {
24768        var sOptionGroup = this.oCommon.getOptionSelectGroup(oOptionChoose);
24769        var sSelector = '.option_add_box_del';
24770        if (sOptionGroup.indexOf('addproduct') < 0) {
24771            sSelector = '.option_box_del';
24772        }
24773        return sSelector;
24774    }
24775};
24776
24777var EC_SHOP_FRONT_NEW_OPTION_EXTRA_ITEMSELECTION = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_ITEMSELECTION');
24778
24779var oSingleSelection = function()
24780{
24781    var sProductTargetKey = null;
24782    var sSingleQuantityInputSelector = 'input.single-quantity-input';
24783    var sIndexKey = '#PRODUCTNUM#';
24784    var sSingleObjectName = 'oSingleItemData['+sIndexKey+']';
24785    var sItemSelectionType = null;
24786
24787    var getTotalPriceSelector = function()
24788    {
24789        return EC$('#totalProducts .total:visible').length > 0 ? '#totalProducts .total' : '.xans-product-detail #totalPrice .total, .xans-product-zoom #totalPrice .total';
24790    };
24791
24792    var isItemSelectionTypeS = function()
24793    {
24794        var bSingleQuantityInputSelector = EC$(sSingleQuantityInputSelector).filter(':visible').length > 0;
24795        // 반응형일시에는 노출되지 않음. 하지만 존재함
24796        if (bSingleQuantityInputSelector === false && CAFE24.checkChannelUI() === true) {
24797            if (sItemSelectionType === 'S') {
24798                return true;
24799            }
24800        }
24801
24802        return bSingleQuantityInputSelector;
24803
24804    };
24805
24806    var isItemSelectionTypeM = function()
24807    {
24808        return EC$(sSingleQuantityInputSelector).filter(':visible').length === 0;
24809    };
24810
24811    var getProductNum = function(oQuantityObject)
24812    {
24813        if (EC$(oQuantityObject).hasClass('single-quantity-input') === true) {
24814            return parseInt(EC$(oQuantityObject).attr('product-no'), 10);
24815        }
24816        if (EC$(oQuantityObject).attr('product-no')) {
24817            return EC$(oQuantityObject).attr('product-no');
24818        }
24819        var sProductNumClass = EC$.grep(EC$(oQuantityObject).attr('class').split(' '), function(sClassName,i) {
24820            return CAFE24.UTIL.trim(sClassName).indexOf('product-no-') === 0;
24821        })[0];
24822        return parseInt(sProductNumClass.replace('product-no-', ''), 10);
24823    };
24824
24825    var getOptionSequenceNum = function(oQuantityObject)
24826    {
24827        if (EC$(oQuantityObject).hasClass('single-quantity-input') === true) {
24828            return parseInt(EC$(oQuantityObject).attr('option-sequence'), 10);
24829        }
24830        if (EC$(oQuantityObject).attr('has-option') === 'F') {
24831            return 1;
24832        }
24833        if (EC$(oQuantityObject).attr('option_type') === 'F' && EC$(oQuantityObject).attr('option_sort_no')) {
24834            return parseInt(EC$(oQuantityObject).attr('option_sort_no'), 10);
24835        }
24836        var sSequenceClass = EC$.grep(EC$(oQuantityObject).attr('class').split(' '), function(sClassName,i) {
24837            return CAFE24.UTIL.trim(sClassName).indexOf('option-sequence-') === 0;
24838        })[0];
24839
24840        return parseInt(sSequenceClass.replace('option-sequence-', ''), 10);
24841    };
24842
24843    var setProductTargetKey = function(oElement, sType)
24844    {
24845        var iTargetProductNum = iProductNo;
24846        var sTargetKey = iProductNo;
24847        var iOptionSequence = 1;
24848        if (typeof(oElement) !== 'undefined') {
24849            if (oElement.hasClass('single-quantity-input') === true || oElement.hasClass('quantity-h
24849andle') === true) {
24850                iTargetProductNum = getProductNum(oElement);
24851                iOptionSequence = getOptionSequenceNum(oElement);
24852            } else {
24853                var oOptionChoose = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setOptionBoxElement(oElement);
24854                iTargetProductNum = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oOptionChoose);
24855                if (isNaN(iTargetProductNum) === true) {
24856                    iTargetProductNum = EC$(oElement).attr('product-no');
24857                }
24858                iOptionSequence = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSortNum(oOptionChoose);
24859            }
24860            sTargetKey = iTargetProductNum;
24861        }
24862        if (sType === 'setproduct') {
24863            if (sSetProductType === 'S') {
24864                sTargetKey = iTargetProductNum+'|'+iProductNo;
24865            }
24866        }
24867        var bAddProductOptionF = false;
24868        var bSetProductOptionF = false;
24869        if (sType === 'addproduct') {
24870            var oOptionChoose = EC$('select#addproduct_option_id_'+iTargetProductNum+'_'+iOptionSequence);
24871            bAddProductOptionF = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionType(oOptionChoose) === 'F';
24872        }
24873        if (sType === 'setproduct') {
24874            var oOptionChoose = EC$('select#setproduct_option_id_'+iTargetProductNum+'_'+iOptionSequence);
24875            bSetProductOptionF = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionType(oOptionChoose) === 'F';
24876        }
24877        if ((typeof(sType) === 'undefined' && option_type === 'F') || bSetProductOptionF === true || bAddProductOptionF === true) {
24878            sTargetKey = sTargetKey+'|'+iOptionSequence;
24879        }
24880        sProductTargetKey = sTargetKey;
24881    };
24882
24883    return {
24884        getProductTargetKey: function()
24885        {
24886            return sProductTargetKey;
24887        },
24888        setProductTargetKey: function(oElement, sType)
24889        {
24890            return setProductTargetKey(oElement, sType);
24891        },
24892        getTotalPriceSelector: function()
24893        {
24894            return getTotalPriceSelector();
24895        },
24896        getProductNum: function(oQuantityButtnObject)
24897        {
24898            return getProductNum(oQuantityButtnObject);
24899        },
24900        getOptionSequence: function(oQuantityButtnObject)
24901        {
24902            return getOptionSequenceNum(oQuantityButtnObject);
24903        },
24904        getQuantityInput: function(oQuantityButtonObject, sContext)
24905        {
24906            var iSequenceNum = getOptionSequenceNum(oQuantityButtonObject);
24907            var iProductNum = getProductNum(oQuantityButtonObject);
24908            if (typeof(sContext) === 'undefined') {
24909                sContext = null;
24910            }
24911
24912            return EC$(sSingleQuantityInputSelector+'[option-sequence='+iSequenceNum+'][product-no='+iProductNum+']', sContext);
24913        },
24914        isItemSelectionTypeS: function()
24915        {
24916            return isItemSelectionTypeS();
24917        },
24918        isItemSelectionTypeM: function()
24919        {
24920            return isItemSelectionTypeM();
24921        },
24922        setItemSelectionType: function(sType)
24923        {
24924            if (typeof(sType) !== 'undefined') {
24925                sItemSelectionType = sType;
24926            }
24927        }
24928    };
24929}();
24930
24931CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET = {
24932    /**
24933     * 장바구니 담기 Ajax완료여부
24934     */
24935    bIsLoadedPriceAjax: false,
24936
24937    /**
24938     * 옵션 선택시 장바구니 바로 담기 기능 사용 여부
24939     */
24940    bIsUseDirectBasket: false,
24941
24942    /**
24943     * 옵션선택후 주석옵션이 선언되어있다면 바로 장바구니담기
24944     * @param oOptionChoose 구분할 옵션박스 object
24945     */
24946    completeCallback: function(oOptionChoose) {
24947        if (this.isAvailableDirectBasket(oOptionChoose) === false) {
24948            return;
24949        }
24950
24951        var oInterval = setInterval(function () {
24952            if (CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET.bIsLoadedPriceAjax === true) {
24953                
24954                //장바구니 담기
24955                product_submit(2, '/exec/front/order/basket/', this);
24956                CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET.bIsLoadedPriceAjax = false;
24957
24958                //옵션박스 제거
24959                EC$('.option_box_del').each(function() {
24960                    EC$(this).trigger('click');
24961                });
24962
24963                //옵션선택값 초기화
24964                EC$('[product_option_area="' + EC$(oOptionChoose).attr('product_option_area') + '"]').each(function() {
24965                    CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this, '*', true, true);
24966                });
24967
24968                clearInterval(oInterval);
24969            }
24970        }, 300);
24971    },
24972
24973    /**
24974     * 사용가능한 상태인지 확인
24975     * @param oOptionChoose 값을 가져오려는 옵션박스 object
24976     * @return boolean true : 사용가능, false : 사용불가
24977     */
24978    isAvailableDirectBasket: function (oOptionChoose) {
24979
24980        if (this.bIsUseDirectBasket === false) {
24981            return false;
24982        }
24983
24984        oOptionChoose = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setOptionBoxElement(oOptionChoose);
24985        if (EC$(oOptionChoose).attr('product_type') !== 'product_option') {
24986            return false;
24987        }
24988
24989        return true;
24990    },
24991
24992    /**
24993     * 옵션선택시 장바구니 바로담기 기능
24994     */
24995    setUseDirectBasket: function () {
24996        this.bIsUseDirectBasket = true;
24997    }
24998};
24999
25000var EC_SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET');
25001
25002CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_FUNDING = {
25003    sCurrentCompositionCode: null,
25004    prefetch: function(oOptionChoose)
25005    {
25006    },
25007    completeCallback: function(oOptionChoose)
25008    {
25009    },
25010    eachCallback: function(oOptionChoose)
25011    {
25012        if (typeof(EC$(oOptionChoose).attr('composition-code')) === 'undefined') {
25013            return true;
25014        }
25015        var sCompositionCode = EC$(oOptionChoose).attr('composition-code');
25016        EC$('input.selected-funding-item[composition-code="'+sCompositionCode+'"]').remove();
25017        this.sCurrentCompositionCode = sCompositionCode;
25018        var sItemCode = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getItemCode(oOptionChoose);
25019        if (sItemCode === false) {
25020            return true;
25021        }
25022        /*
25023        var oItemCode = EC$('<input>').attr({
25024            'type' : 'hidden',
25025            'composition-code' : sCompositionCode
25026        }).addClass('selected-funding-item option_box_id').val(sItemCode);
25027        EC$('.EC-funding-checkbox[value="'+sCompositionCode+'"]').append(oItemCode);
25028         */
25029    }
25030};
25031
25032var EC_SHOP_FRONT_NEW_OPTION_EXTRA_FUNDING = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_EXTRA_FUNDING');
25033
25034/**
25035 * 재고플러스 사용하는 몰 상품상세 재고현황 표시
25036 */
25037CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS = {
25038
25039    /**
25040     * 조합 분리형 옵션일경우 마지막 옵션 선택시 추가되어야할 문구
25041     * @param iProductNo 상품번호
25042     * @param sItemCode 품목코드
25043     * @returns string 재고현황 텍스트
25044     */
25045    get: function(iProductNo, sItemCode) {
25046        return this.getStockStatus(iProductNo, sItemCode);
25047    },
25048
25049    /**
25050     * 재고 현황 텍스트 반환
25051     * @param iProductNo 상품번호
25052     * @param sItemCode 아이템코드
25053     * @returns string 재고 현황 텍스트
25054     */
25055    getStockStatus: function(iProductNo, sItemCode) {
25056        var sStockText = '';
25057        var oStock;
25058
25059        if (this.getCheckUseWms() === false) {
25060            return sStockText;
25061        }
25062
25063        var oResponse = CAFE24.SHOP_FRONT_NEW_PRODUCT_WMS_STOCK_STATUS.aApiResponseData;
25064        if (EC$.inArray(iProductNo.toString(), Object.keys(oResponse)) < 0) {
25065            return sStockText;
25066        }
25067
25068        if (typeof sItemCode === 'undefined') {
25069            oStock = oResponse[iProductNo]['product'];
25070        } else {
25071            if (EC$.inArray(sItemCode, Object.keys(oResponse[iProductNo]['option'])) < 0) {
25072                return sStockText;
25073            }
25074            oStock = oResponse[iProductNo]['option'][sItemCode];
25075        }
25076
25077        sStockText = typeof oStock['text'] === 'string' ? ' [' +oStock['text'] + ']' : sStockText;
25078        return sStockText;
25079    },
25080
25081    /**
25082     * 재고플러스 사용여부
25083     * @returns boolean
25084     */
25085    getCheckUseWms: function() {
25086        return typeof CAFE24.SHOP_FRONT_NEW_PRODUCT_WMS_STOCK_STATUS !== 'undefined' && CAFE24.SHOP_FRONT_NEW_PRODUCT_WMS_STOCK_STATUS.hasOwnProperty('aApiResponseData') === true;
25087    },
25088
25089    /**
25090     * 재고 현황 텍스트 설정
25091     */
25092    setStockStatus: function() {
25093        if (this.getCheckUseWms() === false) {
25094            return;
25095        }
25096
25097        if (has_option === 'T') {
25098            CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS.setHasOptionT();
25099        } else {
25100            CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS.setHasOptionF();
25101        }
25102    },
25103
25104    /**
25105     * 옵션 있는 상품 재고현황 텍스트 설정 (조합 일체형)
25106     */
25107    setHasOptionT: function() {
25108        EC$('select[option_select_element="ec-option-select-finder"], ul[option_select_element="ec-option-select-finder"]').each(function() {
25109            var oThis = this;
25110            var iProductNo = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oThis);
25111
25112            if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(oThis) === true) {
25113                EC$(oThis).find('li').each(function() {
25114                    var sOptionText = EC$(this).attr('title') + CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS.getStockStatus(iProductNo, EC$(this).attr('option_value'));
25115                    EC$(this).attr('title', sOptionText);
25116                });
25117            } else {
25118                EC$(oThis).find('option').each(function() {
25119                    var sOptionText = EC$(this).text() + CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS.getStockStatus(iProductNo, EC$(this).val());
25120                    EC$(this).text(sOptionText);
25121                });
25122            }
25123        });
25124    },
25125
25126    /**
25127     * 옵션 없는 상품 재고현황 텍스트 설정
25128     */
25129    setHasOptionF: function() {
25130        var oStockText = EC$('.ec_product_stockText');
25131        if (oStockText.length < 1) {
25132            return;
25133        }
25134
25135        var sStockText = CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS.getStockStatus(iProductNo);
25136        if (sStockText === '') {
25137            return;
25138        }
25139
25140        var sProductStockText = oStockText.html() + sStockText;
25141        oStockText.html(sProductStockText);
25142    }
25143
25144};
25145
25146
25147/**
25148 * 뉴상품 옵션 셀렉트 공통파일
25149 */
25150CAFE24.SHOP_FRONT_NEW_OPTION_COMMON = {
25151    cons: null,
25152
25153    data: null,
25154
25155    bind: null,
25156
25157    validation: null,
25158
25159    /**
25160     * 페이지 로드가 완료되었는지
25161     */
25162    isLoad: false,
25163
25164    initObject: function() {
25165        this.cons = CAFE24.SHOP_FRONT_NEW_OPTION_CONS;
25166        this.data = CAFE24.SHOP_FRONT_NEW_OPTION_DATA;
25167        this.bind = CAFE24.SHOP_FRONT_NEW_OPTION_BIND;
25168        this.validation = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION;
25169    },
25170
25171    /**
25172     * 페이지 로딩시 초기화
25173     */
25174    init: function() {
25175        var oThis = this;
25176        //조합분리형이지만 옵션이 1개인경우
25177        var bIsSolidOption = false;
25178        //첫 로드시에는 첫번째 옵션만 검색
25179        EC$('select[option_select_element="ec-option-select-finder"][option_sort_no="1"], ul[option_select_element="ec-option-select-finder"][option_sort_no="1"]').each(function() {
25180            //연동형이 아닌고 분리형일때만 실행
25181            bIsSolidOption = false;
25182            if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isSeparateOption(this) === true) {
25183                if (Olnk.isLinkageType(EC$(this).attr('option_type')) === false) {
25184                    if (parseInt(EC$('[product_option_area="'+oThis.getOptionSelectGroup(this)+'"]').length) < 2) {
25185                        bIsSolidOption = true;
25186                    }
25187
25188                    oThis.data.initializeSoldoutFlag(EC$(this));
25189
25190                    oThis.setOptionText(EC$(this), bIsSolidOption);
25191                }
25192            }
25193        });
25194    },
25195
25196    /**
25197     * 옵션상품인데 모든옵션이 판매안함+진열안함일때 예외처리
25198     * @param sProductOptionID 옵션 Selectbox ID
25199     */
25200    isValidOptionDisplay: function(sProductOptionID)
25201    {
25202        var iOptionCount = 0;
25203        EC$('select[option_select_element="ec-option-select-finder"][id^="' + sProductOptionID + '"], ul[option_select_element="ec-option-select-finder"][ec-dev-id^="' + sProductOptionID + '"]').each(function() {
25204
25205            if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(this) === true) {
25206                iOptionCount += EC$(this).find('li').length;
25207            } else {
25208                iOptionCount += EC$(this).find('option').length - 2;
25209            }
25210        });
25211
25212        return iOptionCount > 0;
25213    },
25214
25215    /**
25216     * 각 옵션에대해 전체품절인지 확인후
25217     */
25218    setOptionText: function(oOptionChoose, bIsSolidOption) {
25219        var bIsStyleButton = this.isOptionStyleButton(oOptionChoose);
25220        var oTargetOption = null;
25221        if (bIsStyleButton === true) {
25222            oTargetOption = EC$(oOptionChoose).find('li');
25223        } else {
25224            oTargetOption = EC$(oOptionChoose).find('option').filter('[value!="*"][value!="**"]');
25225        }
25226
25227        var bIsDisplaySolout = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isSoldoutOptionDisplay();
25228        var iProductNum = this.getOptionProductNum(oOptionChoose);
25229        var oThis = this;
25230
25231        EC$(oTargetOption).each(function() {
25232            var sValue = oThis.getOptionValue(oOptionChoose, EC$(this));
25233            var isSoldout = CAFE24.SHOP_FRONT_NEW_OPTION_DATA.getSoldoutFlag(iProductNum, sValue);
25234            var bIsDisplay = CAFE24.SHOP_FRONT_NEW_OPTION_DATA.getDisplayFlag(iProductNum, sValue);
25235            var sOptionText = oThis.getOptionText(oOptionChoose, this);
25236
25237            if (bIsDisplay === false) {
25238                EC$(this).remove();
25239                return;
25240            }
25241
25242            //조합분리형인데 옵션이 1개인경우 옵션추가금액을 세팅)
25243            if (bIsSolidOption === true) {
25244                var sItemCode = oThis.data.getItemCode(iProductNum, sValue);
25245
25246                var sAddText = CAFE24.SHOP_FRONT_NEW_OPTION_BIND.setAddText(iProductNum, sItemCode, oOptionChoose);
25247                if (sAddText !== '') {
25248                    sOptionText = sOptionText + sAddText;
25249                }
25250            }
25251
25252            if (isSoldout === true) {
25253                //품절표시안함일때 안보여주도록함(첫번째옵션이라서.. 어쩔수없이 여기서 ㅋ)
25254                //두번째옵션부터는 동적생성이니깐 bind에서처리
25255                if (bIsDisplaySolout === false) {
25256                    EC$(this).remove();
25257                    return;
25258                }
25259                //해당 옵션값 객체가 넘어오면 바로 적용
25260                if (bIsStyleButton === true && CAFE24.checkChannelUI() === false) {
25261                    EC$(this).addClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_SOLDOUT_CLASS);
25262                }
25263
25264                //분리형이면서 전체상품이 품절이면
25265                if (bIsSolidOption !== true) {
25266                    var sSoldoutText = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getSoldoutText(oOptionChoose, sValue);
25267                    sOptionText = sOptionText + ' ' + sSoldoutText;
25268
25269                }
25270            }
25271
25272            oThis.setText(this, sOptionText);
25273
25274        });
25275    },
25276
25277    /**
25278     * 품목이 아닌 각 옵션별로 전체품절인지 황니후 품절이면 품절문구 반환
25279     * @param oOptionChoose
25280     * @param sValue
25281     * @returns {String}
25282     */
25283    getSoldoutText: function(oOptionChoose, sValue) {
25284        var sText = '';
25285
25286        var iProductNum = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionProductNum(oOptionChoose);
25287
25288        if (CAFE24.SHOP_FRONT_NEW_OPTION_DATA.getSoldoutFlag(iProductNum, sValue) === true) {
25289            return '[' + CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_SOLDOUT.getSoldoutDiplayText(iProductNum) + ']';
25290        }
25291
25292        return sText;
25293    },
25294
25295    /**
25296     * 셀렉트박스형 옵션인지 버튼형 옵션이지 확인
25297     * @param oOptionChoose 구분할 옵션박스 object
25298     * @returns true => 버튼형옵션, false => 기존 select형 옵션
25299     */
25300    isOptionStyleButton: function(oOptionChoose) {
25301        var sOptionStyle = EC$(oOptionChoose).attr(this.cons.OPTION_STYLE);
25302        if (sOptionStyle === 'preview' || sOptionStyle === 'button' || sOptionStyle === 'radio') {
25303            return true;
25304        }
25305
25306        return false;
25307    },
25308
25309    /**
25310     * div 셀렉트박스형 옵션인지 확인
25311     * @param oOptionChoose 구분할 옵션박스 object
25312     * @returns true => 버튼형옵션, false => 기존 select형 옵션
25313     */
25314    isOptionStyleDivSelect: function(oOptionChoose) {
25315        return (EC$(oOptionChoose).hasClass(this.cons.OPTION_STYLE_DIV_SELECT_CLASS) === true) ? true : false;
25316    },
25317
25318    /**
25319     * 해당 옵션의 옵션출력타입(분리형 : S, 일체형 : C)
25320     * @param oOptionChoose 구분할 옵션박스 object
25321     * @returns 옵션타입
25322     */
25323    getOptionListingType: function(oOptionChoose)
25324    {
25325        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25326        return EC$(oOptionChoose).attr(this.cons.OPTION_LISTING_TYPE);
25327    },
25328
25329    /**
25330     * 해당 옵션의 옵션타입(조합형 : T, 연동형 : E, 독립형 : F)
25331     * @param oOptionChoose 구분할 옵션박스 object
25332     * @returns 옵션타입
25333     */
25334    getOptionType: function(oOptionChoose) {
25335        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25336        return EC$(oOptionChoose).attr(this.cons.OPTION_TYPE);
25337    },
25338
25339    /**
25340     * 해당 옵션의 옵션그룹명을 가져온다
25341     * @param oOptionChoose 구분할 옵션박스 object
25342     * @returns 옵션그룹이름
25343     */
25344    getOptionSelectGroup: function(oOptionChoose) {
25345        return EC$(oOptionChoose).attr(this.cons.GROUP_ATTR_NAME);
25346    },
25347
25348    /**
25349     * sOptionStyleConfirm 에 해당하는 옵션인지 확인
25350     * @param oOptionChoose 구분할 옵션박스 object
25351     * @param sOptionStyleConfirm 옵션스타일(CAFE24.SHOP_FRONT_NEW_OPTION_CONS : OPTION_STYLE_PREVIEW 또는 OPTION_STYLE_BUTTON)
25352     * @return boolean 확인결과
25353     */
25354    isOptionStyle: function(oOptionChoose, sOptionStyleConfirm) {
25355        var sOptionStype = EC$(oOptionChoose).attr(this.cons.OPTION_STYLE);
25356        if (sOptionStype === sOptionStyleConfirm) {
25357            return true;
25358        }
25359
25360        return false;
25361    },
25362
25363    /**
25364     * 해당 옵션의 선택된 Text내용을 가져옴
25365     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25366     * @returns 옵션 내용Text
25367     */
25368    getOptionSelectedText: function(oOptionChoose) {
25369        if (this.isOptionStyleButton(oOptionChoose) === true) {
25370            return EC$(oOptionChoose).find('li.' + this.cons.BUTTON_OPTION_SELECTED_CLASS).attr('title');
25371        } else {
25372            return EC$(oOptionChoose).find('option:selected').text();
25373        }
25374    },
25375
25376    /**
25377     * 해당 옵션의 선택된 값을 가져옴
25378     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25379     * @returns string 옵션값
25380     */
25381    getOptionSelectedValue: function(oOptionChoose) {
25382        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25383
25384        if (this.isOptionStyleButton(oOptionChoose) === true) {
25385            var oTarget = EC$(oOptionChoose).find('li.' + this.cons.BUTTON_OPTION_SELECTED_CLASS);
25386
25387            //버튼형옵션은 *, **값이 없기떄문에 선택된게 없다면 강제리턴
25388            if (oTarget.length < 1) {
25389                return '*';
25390            } else {
25391                return oTarget.attr('option_value');
25392            }
25393        } else {
25394            var sValue = EC$(oOptionChoose).val();
25395            return (CAFE24.UTIL.trim(sValue) === '') ? '*' : sValue;
25396        }
25397    },
25398
25399    /**
25400     * 해당 Element의 값을 가져옴
25401     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25402     * @param oOptionChooseElement 값을 가져오려는 옵션 항목
25403     * @returns string 옵션값
25404     */
25405    getOptionValue: function(oOptionChoose, oOptionChooseElement) {
25406        if (this.isOptionStyleButton(oOptionChoose) === true) {
25407            return EC$(oOptionChooseElement).attr('option_value');
25408        } else {
25409            return EC$(oOptionChooseElement).val();
25410        }
25411    },
25412
25413    /**
25414     * 해당 Element의 Text값을 가져옴
25415     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25416     * @param oOptionChooseElement 값을 가져오려는 옵션 항목
25417     * @returns string 옵션값
25418     */
25419    getOptionText: function(oOptionChoose, oOptionChooseElement) {
25420        if (this.isOptionStyleButton(oOptionChoose) === true) {
25421            return EC$(oOptionChooseElement).attr('title');
25422        } else {
25423            return EC$(oOptionChooseElement).text();
25424        }
25425    },
25426
25427    /**
25428     * 선택된 옵션의 Element를 가져온다
25429     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25430     * @returns 선택옵션의 DOM Element
25431     */
25432    getOptionSelectedElement: function(oOptionChoose) {
25433        if (this.isOptionStyleButton(oOptionChoose) === true) {
25434            return EC$(oOptionChoose).find('li.' + this.cons.BUTTON_OPTION_SELECTED_CLASS);
25435        } else {
25436            return EC$(oOptionChoose).find('option:selected');
25437        }
25438    },
25439
25440    getOptionLastSelectedElement: function(sOptionGroup)
25441    {
25442        var oOptionGroup = this.getGroupOptionObject(sOptionGroup);
25443        var aTempResult = [];
25444        oOptionGroup.each(function(i) {
25445            if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectedValue(oOptionGroup[i]) !== '*') {
25446                aTempResult.push(oOptionGroup[i]);
25447            }
25448        });
25449        return EC$(aTempResult[aTempResult.length - 1]);
25450    },
25451
25452    /**
25453     * 해당 옵션의 상품번호를 가져옴
25454     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25455     * @returns int 상품번호
25456     */
25457    getOptionProductNum: function(oOptionChoose) {
25458        return parseInt(EC$(oOptionChoose).attr(this.cons.OPTION_PRODUCT_NUM));
25459    },
25460
25461    /**
25462     * 해당 옵션의 순번을 가져옴
25463     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25464     * @returns int 해당 옵션의 순서 번호
25465     */
25466    getOptionSortNum: function(oOptionChoose) {
25467        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25468        return parseInt(EC$(oOptionChoose).attr(this.cons.OPTION_SORT_NUM));
25469    },
25470
25471    /**
25472     * 이벤트 옵션까지에대해 현재까지 선택된 옵션값 배열
25473     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25474     * @param bIsString 값이 true이면 선택된 옵션들을 구분자로 join해서 받아온다
25475     * @returns 현재까지 선택된 옵션값 배열
25476     */
25477    getAllSelectedValue: function(oOptionChoose, bIsString) {
25478        var iOptionSortNum = this.getOptionSortNum(oOptionChoose);
25479
25480        //지금까지 선택된 옵션의 값
25481        var aSelectedValue = [];
25482        EC$('[product_option_area="'+EC$(oOptionChoose).attr(this.cons.GROUP_ATTR_NAME)+'"]').each(function() {
25483            if (parseInt(EC$(this).attr('option_sort_no')) <= iOptionSortNum) {
25484                aSelectedValue.push(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSelectedValue(EC$(this)));
25485            }
25486        });
25487
25488        return (bIsString === true) ? aSelectedValue.join(this.cons.OPTION_GLUE) : aSelectedValue;
25489    },
25490
25491    /**
25492     * iSelectedOptionSortNum 의 하위옵션을 초기화(0일때는 모두초기화)ㅅ
25493     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25494     * @param iSelectedOptionSortNum 하위옵션을 초기화할 대상 옵션 순번
25495     */
25496    setInitializeDefault: function(oOptionChoose, iSelectedOptionSortNum) {
25497        var sOptionGroup = EC$(oOptionChoose).attr(this.cons.GROUP_ATTR_NAME);
25498        var iProductNum = this.getOptionProductNum(oOptionChoose);
25499        this.bind.setInitializeDefault(sOptionGroup, iSelectedOptionSortNum, iProductNum);
25500    },
25501
25502    /**
25503     * 외부에서 기존스크립트가 호출할때는 버튼형옵션객체가 아니라 숨겨진 셀렉트박스에서 호출하므로 버튼형옵션객체를 찾아서 리턴
25504     */
25505    setOptionBoxElement: function(oOptionChoose) {
25506        if (typeof(EC$(oOptionChoose).attr('product_option_area_select')) !== 'undefined') {
25507            oOptionChoose = EC$('ul[product_option_area="'+EC$(oOptionChoose).attr('product_option_area_select')+'"][ec-dev-id="'+EC$(oOptionChoose).attr('id')+'"]');
25508        }
25509
25510        return oOptionChoose;
25511    },
25512
25513    /**
25514     * 선택한 옵션 하위옵션 모두 초기화(추가구성상품에서 연동형옵션때문에...)
25515     * @param oOptionChoose
25516     */
25517    setAllClear: function(oOptionChoose) {
25518        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25519
25520        var iSortNo = parseInt(this.getOptionSortNum(oOptionChoose));
25521        EC$(this.getGroupOptionObject(this.getOptionSelectGroup(oOptionChoose))).each(function() {
25522            if (iSortNo < parseInt(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSortNum(EC$(this)))) {
25523                CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(EC$(this), '*');
25524            }
25525        });
25526    },
25527
25528    /**
25529     * 멀티옵션(구스킨)에서 사용할때 해당 옵션의 id값을 바꾸는기능이 있어서 추가
25530     * @param oOptionChooseOrg
25531     * @param sId
25532     */
25533    setID: function(oOptionChooseOrg, sId) {
25534        if (EC$(oOptionChooseOrg).attr('option_style') === 'select') {
25535            oOptionChoose = oOptionChooseOrg;
25536        } else {
25537            oOptionChoose = EC$(oOptionChooseOrg).parent().find('ul[option_style="preview"], [option_style="button"], [option_style="radio"]');
25538        }
25539
25540        if (this.isOptionStyleButton(oOptionChoose) === true) {
25541            EC$(oOptionChoose).attr('ec-dev-id', sId);
25542            EC$(oOptionChooseOrg).attr('id', sId);
25543        } else {
25544            EC$(oOptionChoose).attr('id', sId);
25545        }
25546    },
25547
25548    /**
25549     * 멀티옵션(구스킨)에서 사용할때 해당 옵션의 id값을 바꾸는기능이 있어서 추가
25550     * @param oOptionChooseOrg
25551     * @param sGroupID
25552     */
25553    setGroupArea: function(oOptionChooseOrg, sGroupID) {
25554        var oOptionChoose = null;
25555        if (EC$(oOptionChooseOrg).attr('option_style') === 'select') {
25556            oOptionChoose = oOptionChooseOrg;
25557        } else {
25558            oOptionChoose = EC$(oOptionChooseOrg).parent().find('ul[option_style="preview"], [option_style="button"], [option_style="radio"]');
25559        }
25560
25561        if (this.isOptionStyleButton(oOptionChoose) === true) {
25562            EC$(oOptionChoose).attr('product_option_area', sGroupID);
25563            EC$(oOptionChooseOrg).attr('product_option_area_select', sGroupID);
25564        } else {
25565            EC$(oOptionChoose).attr('product_option_area', sGroupID);
25566        }
25567    },
25568
25569    /**
25570     * 해당 선택한 옵션의 text값을 세팅
25571     */
25572    setText: function(oSelectecOptionChoose, sText) {
25573        oOptionChoose = this.setOptionBoxElement(EC$(oSelectecOptionChoose).parent());
25574
25575        if (this.isOptionStyleButton(oOptionChoose) === true) {
25576            var sValue = EC$(oSelectecOptionChoose).attr('option_value');
25577            var oTarget = EC$(oOptionChoose).find('li[option_value="'+sValue+'"]');
25578            EC$(oTarget).attr('title', sText);
25579        }
25580
25581        if (sYtshopping === 'T' || sIsYoutubeShops === 'T') {
25582            // 조합분리형, 옵션이 한개, 유튜브샵스인경우
25583            EC$(oTarget).find('a>span').text(sText);
25584        }
25585
25586        if (this.isOptionStyleButton(EC$(oSelectecOptionChoose).parent()) !== true) {
25587            EC$(oSelectecOptionChoose).text(sText);
25588        }
25589    },
25590
25591    /**
25592     * 추가 이미지에서 추출한 품목 코드를 바탕으로 옵션 선택
25593     * @param sItemCode 품목 코드
25594     */
25595    setValueByAddImage: function(sItemCode) {
25596        if (typeof(sItemCode) === 'undefined') {
25597            return;
25598        }
25599
25600        this.selectItemCode('product_option_' + iProductNo + '_0', sItemCode);
25601    },
25602
25603    /**
25604     * 외부에서 옵션을 선택하는걸 호출할 경우 해당 옵션의 product_option_area값과 품목코드를 전달
25605     * @param sOptionArea 옵션 element의 product_option_area값 attribute값
25606     * @param sItemCode 품목코드
25607     */
25608    selectItemCode: function(sOptionArea, sItemCode)
25609    {
25610        var oSelect = EC$('[product_option_area="' + sOptionArea + '"]');
25611        oSelect = this.setOptionBoxElement(oSelect);
25612
25613        var sOptionListType = this.getOptionListingType(oSelect);
25614        var sOptionType = this.getOptionType(oSelect);
25615
25616        //조합일체형이나 독립형인경우
25617        if (sOptionListType === 'C' || sOptionType === 'F') {
25618            this.setValue(oSelect, sItemCode, true, true);
25619        } else {
25620            var iProductNo = this.getOptionProductNum(oSelect);
25621            var oItemData = this.getProductStockData(iProductNo);
25622
25623            if (oItemData === null) {
25624                return;
25625            }
25626
25627            if (oItemData.hasOwnProperty(sItemCode) === false) {
25628                return;
25629            }
25630
25631            var aOptionValue = oItemData[sItemCode].option_value_orginal;
25632
25633            oSelect.each(function (i) {
25634                CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this, aOptionValue[i], true, true);
25635            });
25636        }
25637    },
25638
25639    /**
25640     * 해당 Element의 값을 강제로 지정
25641     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25642     * @param sValue set 하려는 value
25643     * @param bIsInitialize false인 경우에는 클릭이벤트를 발생하지 않도록 한다
25644     * @param bChange change 이벤트 발생 여부
25645     */
25646    setValue: function(oOptionChoose, sValue, bIsInitialize, bChange) {
25647        // 값 세팅시 각 페이지에서 EC$(this).val()로 값을 지정할경우
25648        // 본래 버튼형 옵션이면 타겟을 버튼형 옵션으로 이어준다
25649        oOptionChoose = this.setOptionBoxElement(oOptionChoose);
25650
25651        if (this.isOptionStyleButton(oOptionChoose) === true) {
25652            //옵션이 선택되어있는상태면 초기화후 선택
25653            if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isOptionSelected(oOptionChoose) === true) {
25654
25655                // div 셀렉트박스형 옵션은 제외하고 실행
25656                if (EC$(oOptionChoose).hasClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_DIV_SELECT_CLASS) === false) {
25657                    EC$(oOptionChoose).find('li.' + this.cons.BUTTON_OPTION_SELECTED_CLASS).trigger('click');
25658                }
25659            }
25660
25661            var oTarget = EC$(oOptionChoose).find('li[option_value="' + sValue + '"]');
25662
25663            if (EC$(oTarget).length > 0) {
25664                EC$(oTarget).trigger('click');
25665            } else {
25666                if (bIsInitialize !== false) {
25667                    // 선택값이 없다면 셀렉트박스 초기화
25668                    var iProductNum = this.getOptionProductNum(oOptionChoose);
25669                    var iSelectedOptionSortNum = this.getOptionSortNum(oOptionChoose);
25670                    var sOptionGroup = this.getOptionSelectGroup(oOptionChoose);
25671                    var bIsRequired = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(oOptionChoose);
25672
25673                    if (CAFE24.SHOP_FRONT_NEW_OPTION_BIND.isEnabledOptionInit(oOptionChoose) === true) {
25674                        CAFE24.SHOP_FRONT_NEW_OPTION_BIND.setInitializeDefault(sOptionGroup, iSelectedOptionSortNum, iProductNum, bIsRequired);
25675                    }
25676
25677                    CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM.eachCallback(oOptionChoose);
25678                    CAFE24.SHOP_FRONT_NEW_OPTION_BIND.setRadioButtonSelect(oTarget, oOptionChoose, false);
25679                }
25680
25681                this.setTriggerSelectbox(oOptionChoose, sValue);
25682            }
25683        } else {
25684            EC$(oOptionChoose).val(sValue);
25685
25686            if (typeof(bChange) !== 'undefined') {
25687                EC$(oOptionChoose).trigger('change');
25688            }
25689        }
25690    },
25691
25692    /**
25693     * 버튼 또는 이미지형 옵션일 경우 동적 selectbox와 동기화 시킴
25694     * @param oOptionChoose 선택한 옵션 Object
25695     * @param sValue set 하려는 value
25696     * @param bIsTrigger 셀렉트박스의 change 이벤트를 발생시키지 않을때(ex:모바일의 옵션선택 레이어..)
25697     */
25698    setTriggerSelectbox: function(oOptionChoose, sValue, bIsTrigger)
25699    {
25700        if (this.isOptionStyleButton(oOptionChoose) === true) {
25701            var oTargetSelect = EC$('select[product_option_area_select="' + EC$(oOptionChoose).attr('product_option_area') + '"][id="' + EC$(oOptionChoose).attr('ec-dev-id') + '"]');
25702            var bChange = true;
25703
25704            var sText = '';
25705            if (this.validation.isItemCode(sValue) === false) {
25706                sValue = '*';
25707                sText = 'empty';
25708
25709                bChange = false;
25710            } else {
25711                sValue = this.getOptionSelectedValue(oOptionChoose);
25712                sText = this.getOptionSelectedText(oOptionChoose);
25713            }
25714
25715            if (sValue !== '*') {
25716                EC$(oTargetSelect).find('option[value="' + sValue + '"]').remove('option');
25717
25718                var sOptionsHtml = this.cons.OPTION_STYLE_SELECT_HTML.replace('[value]', sValue).replace('[text]', sText);
25719
25720                EC$(oTargetSelect).append(EC$(sOptionsHtml));
25721            }
25722
25723            EC$(oTargetSelect).val(sValue);
25724
25725            if (bChange === true && bIsTrigger !== false) {
25726                EC$(oTargetSelect).trigger('change');
25727            }
25728        }
25729    },
25730
25731    /**
25732     * 해당 상품의 옵션 재고 관련 데이터를 리턴
25733     * @param iProductNum 상품번호
25734     * @returns option_stock_data 데이터
25735     */
25736    getProductStockData: function(iProductNum) {
25737        return this.data.getProductStockData(iProductNum);
25738    },
25739
25740    /**
25741     * 선택상품의 아이템코드를 반환(선택이 안되어있다면 false)
25742     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25743     * @returns 아이템 코드 OR false
25744     */
25745    getItemCode: function(oOptionChoose) {
25746        //분리조합형일경우
25747        if (this.validation.isSeparateOption(oOptionChoose) === true) {
25748            var sSelectedValue = this.getAllSelectedValue(oOptionCh
25748oose, true);
25749            var iProductNum = this.getOptionProductNum(oOptionChoose);
25750            return this.data.getItemCode(iProductNum, sSelectedValue);
25751        }
25752
25753        //그외의 경우에는 현재 선택된 옵션의 value가 아이템코드
25754        var sItemCode = this.getOptionSelectedValue(oOptionChoose);
25755
25756        return (this.validation.isItemCode(sItemCode) === true) ? sItemCode : false;
25757    },
25758
25759    /**
25760     * 해당 그룹내의 모든옵션에대해 선택된 품목코드를 반환
25761     * @param sOptionGroup 옵션 그룹 (@see : CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS)
25762     * @param bIsAbleSoldout 품절품목에 대한 아이템코드도 포함
25763     * @returns array 선택된 아이템코드 배열
25764     */
25765    getGroupItemCodes: function(sOptionGroup, bIsAbleSoldout) {
25766        var aItemCode = [];
25767        var sItemCode = '';
25768        var oTarget = EC$('[' + this.cons.GROUP_ATTR_NAME + '^="' + sOptionGroup + '"]');
25769
25770        //뉴스킨인 경우에는 옵션박스 레이어에 생성된 input에서 가져온다
25771        if (isNewProductSkin() === true) {
25772            EC$('.' + CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS.DETAIL_OPTION_BOX_PREFIX).each(function() {
25773                //옵션박스에 생성된 input태그이므로 val()로 가져온다
25774                sItemCode = EC$(this).val();
25775                if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isItemCode(sItemCode) === true) {
25776                    aItemCode.push(sItemCode);
25777                }
25778            });
25779
25780            //품절품목에 대한 아이템코드도 포함시킨다 - 현재는 관심상품담을경우에 쓰이는것으로 보임
25781            if (bIsAbleSoldout === true) {
25782                EC$('.' + CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS.DETAIL_OPTION_BOX_SOLDOUT_PREFIX).each(function() {
25783                    aItemCode.push(EC$(this).val());
25784
25785                    if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isItemCode(sItemCode) === true) {
25786                        aItemCode.push(sItemCode);
25787                    }
25788                });
25789            }
25790        } else {
25791            //구스킨인 경우에는 해당하는 옵션에 선택된 값만 가져옴
25792            EC$(oTarget).each(function() {
25793                sItemCode = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getItemCode(this);
25794
25795                //이미 저장된 아이템코드이면 제와(분리형인경우 같은 값이 여러개 들어올수있음)
25796                //조합형을 따로 처리하기보다는 그냥 두는게 더 간단하다는 핑계임
25797                if (EC$.inArray(sItemCode, aItemCode) > -1) {
25798                    return true;//continue
25799                }
25800
25801                if (sItemCode !== false) {
25802                    aItemCode.push(sItemCode);
25803                }
25804            });
25805        }
25806
25807        return aItemCode;
25808    },
25809
25810    /**
25811     * 해당 품목의 품절 여부
25812     * @param iProductNum 상품번호
25813     * @param sItemCode 품목코드
25814     * @returns boolean 품절여부
25815     */
25816    isSoldout: function(iProductNum, sItemCode) {
25817        var aStockData = this.getProductStockData(iProductNum);
25818
25819        if (typeof(aStockData[sItemCode]) === 'undefined') {
25820            return false;
25821        }
25822
25823        //재고를 사용하고 재고수량이 1개미만이면 품절
25824        if (aStockData[sItemCode].use_stock === true && parseInt(aStockData[sItemCode].stock_number) < 1) {
25825            return true;
25826        }
25827
25828        //판매안함 상태이면 품절
25829        if (aStockData[sItemCode].is_selling === 'F') {
25830            return true;
25831        }
25832
25833        return false;
25834    },
25835
25836    /**
25837     * 진열여부 확인
25838     */
25839    isDisplay: function(iProductNum, sItemCode) {
25840        var aStockData = this.getProductStockData(iProductNum);
25841
25842        if (typeof(aStockData[sItemCode]) === 'undefined') {
25843            return false;
25844        }
25845
25846        if (aStockData[sItemCode].is_display !== 'T') {
25847            return false;
25848        }
25849
25850        return true;
25851    },
25852
25853    /**
25854     * sOptionGroup에 해당하는 옵션셀렉트박스의 Element를 가져온다
25855     * @param sOptionGroup sOptionGroup 옵션 그룹 (@see : CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS)
25856     * @returns 해당 옵션셀렉트박스 Element전체
25857     */
25858    getGroupOptionObject: function(sOptionGroup) {
25859        return EC$('[' + this.cons.GROUP_ATTR_NAME + '^="' + sOptionGroup + '"]');
25860    },
25861
25862    /**
25863     * 해당 옵션그룹에서 필수옵션의 갯수를 가져온다
25864     * @param sOptionGroup sOptionGroup 옵션 그룹 (@see : CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS)
25865     * @returns 필수옵션 갯수
25866     */
25867    getRequiredOption: function(sOptionGroup) {
25868        return this.getGroupOptionObject(sOptionGroup).filter('[required="true"],[required="required"]');
25869    },
25870
25871    /**
25872     * 해당 옵션의 전체 Value값을 가져옴(옵션그룹이 아니라 단일 옵션 셀렉츠박스)
25873     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25874     * @returns {Array}
25875     */
25876    getAllOptionValues: function(oOptionChoose) {
25877        //일반 셀렉트박스일때
25878        var aOptionValue = [];
25879        if (this.isOptionStyleButton(oOptionChoose) === false) {
25880            EC$(oOptionChoose).find('option[value!="*"][value!="**"]').each(function() {
25881                aOptionValue.push(EC$(this).val());
25882            });
25883        } else {
25884            //버튼형 옵션일경우
25885            EC$(oOptionChoose).find('li[option_value!="*"][option_value!="**"]').each(function() {
25886                aOptionValue.push(EC$(this).attr('option_value'));
25887            });
25888        }
25889
25890        return aOptionValue;
25891    },
25892
25893    /**
25894     * 해당 옵션의 실제 id값을 리턴
25895     * @param oOptionChoose 값을 가져오려는 옵션박스 object
25896     * @returns {String}
25897     */
25898    getOptionChooseID: function(oOptionChoose) {
25899        var sID = '';
25900        if (this.isOptionStyleButton(oOptionChoose) === true) {
25901            sID = EC$(oOptionChoose).attr('ec-dev-id');
25902        } else {
25903            sID = EC$(oOptionChoose).attr('id');
25904        }
25905
25906        return sID;
25907    },
25908    selectOptionItem: function (sItemCode) {
25909        try {
25910            if (typeof ProductSet !== 'undefined') {
25911                return ProductSet.selectOptionItem(sItemCode);
25912            }
25913
25914            if (typeof option_stock_data === 'undefined') {
25915                return;
25916            }
25917
25918            var aStockData = CAFE24.UTIL.parseJSON(option_stock_data);
25919
25920            if (typeof aStockData[sItemCode] === 'undefined') {
25921                return;
25922            }
25923
25924            EC_SHOP_FRONT_NEW_OPTION_COMMON.selectItemCode(CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS.DETAIL_OPTION_GROUP_ID + iProductNo + '_0', sItemCode);
25925        } catch (e) {
25926            console.error("option select error", e);
25927        }
25928    }
25929};
25930
25931var EC_SHOP_FRONT_NEW_OPTION_COMMON = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_COMMON');
25932
25933EC$(function() {
25934    CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isLoad = true;
25935
25936    //표시된 옵션 선택박스에 대해  디폴트 옵션데이터 정리
25937    CAFE24.SHOP_FRONT_NEW_OPTION_DATA.setDefaultData();
25938
25939    CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.init();
25940});
25941/**
25942 * 옵션에대한 Attribute 및 구분자 모음
25943 */
25944CAFE24.SHOP_FRONT_NEW_OPTION_CONS = {
25945    /**
25946     * 옵션 그룹 Attribute Key(각 상품 및 영역별 구분을 위한 값)
25947     */
25948    GROUP_ATTR_NAME: 'product_option_area',
25949
25950    /**
25951     * 옵션 스타일 Attribute Key
25952     */
25953    OPTION_STYLE: 'option_style',
25954
25955    /**
25956     * 상품번호 Attribute Key
25957     */
25958    OPTION_PRODUCT_NUM: 'option_product_no',
25959
25960    /**
25961     * 각 옵션의 옵션순서 Attribute Key
25962     */
25963    OPTION_SORT_NUM: 'option_sort_no',
25964
25965    /**
25966     * 옵션 타입 Attribute Key
25967     */
25968    OPTION_TYPE: 'option_type',
25969
25970    /**
25971     * 옵션 출력 타입 Attribute Key
25972     */
25973    OPTION_LISTING_TYPE: 'item_listing_type',
25974
25975    /**
25976     * 옵션 값 구분자
25977     */
25978    OPTION_GLUE: '#$%',
25979
25980    /**
25981     * 미리보기형 옵션
25982     */
25983    OPTION_STYLE_PREVIEW: 'preview',
25984
25985    /**
25986     * 버튼형 옵션
25987     */
25988    OPTION_STYLE_BUTTON: 'button',
25989
25990    /**
25991     * 기존 셀렉트박스형 옵션
25992     */
25993    OPTION_STYLE_SELECT: 'select',
25994
25995    /**
25996     * 라디오박스형 옵션
25997     */
25998    OPTION_STYLE_RADIO: 'radio',
25999
26000    /**
26001     * 각 옵션마다 연결된 이미지 Attribute
26002     */
26003    OPTION_LINK_IMAGE: 'link_image',
26004
26005    /**
26006     * 셀렉트박스형 옵션의 Template
26007     */
26008    OPTION_STYLE_SELECT_HTML: '<option value="[value]">[text]</option>',
26009
26010    /**
26011     * 기본 품절 문구
26012     */
26013    OPTION_SOLDOUT_DEFAULT_TEXT: __("품절"),
26014
26015    /**
26016     * 버튼형 옵션의 품절표시 class
26017     */
26018    BUTTON_OPTION_SOLDOUT_CLASS: 'ec-product-soldout',
26019
26020    /**
26021     * 버튼형 옵션의 선택불가 class
26022     */
26023    BUTTON_OPTION_DISABLE_CLASS: 'ec-product-disabled',
26024
26025    /**
26026     * 버튼형 옵션의 선택된 옵션값을 구분하기위한 상수
26027     */
26028    BUTTON_OPTION_SELECTED_CLASS: 'ec-product-selected',
26029
26030    /**
26031     * div 셀렉트박스형 옵션 class
26032     */
26033    OPTION_STYLE_DIV_SELECT_CLASS: 'ec-option-div-select',
26034
26035    /**
26036     * 세트상품 옵션없음 class
26037     */
26038    OPTION_SET_PRODUCT_NONE_CLASS: 'ec-setproduct-option-none'
26039};
26040
26041var EC_SHOP_FRONT_NEW_OPTION_CONS = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_CONS');
26042
26043/**
26044 * 각 옵션그룹에 대한 Key 정의
26045 */
26046CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS = {
26047    /**
26048     * 상품디테일의 메인 옵션 그룹
26049     */
26050    DETAIL_OPTION_GROUP_ID: 'product_option_',
26051
26052    /**
26053     * 뉴스킨 상품상세의 옵션선택시 쩔어지는 옵션박스레이어 class명
26054     */
26055    DETAIL_OPTION_BOX_PREFIX: 'option_box_id',
26056
26057    /**
26058     * 뉴스킨 상품상세의 옵션선택시 쩔어지는 옵션박스레이어 class명(품절일경우의 prefix)
26059     * Prefix존누 많음
26060     */
26061    DETAIL_OPTION_BOX_SOLDOUT_PREFIX: 'soldout_option_box_id'
26062};
26063
26064var EC_SHOP_FRONT_NEW_OPTION_GROUP_CONS = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_GROUP_CONS');
26065
26066CAFE24.SHOP_FRONT_NEW_OPTION_BIND = {
26067
26068    /**
26069     * 선택한 옵션 그룹(product_option_상품번호 : 상품상세일반상품)
26070     */
26071    sOptionGroup: null,
26072
26073    /**
26074     * 옵션이 모두 선택되었을때 해당하는 item_code를 Set
26075     */
26076    sItemCode: false,
26077
26078    /**
26079     * 선택한 옵션의 상품번호
26080     */
26081    iProductNum: 0,
26082
26083    /**
26084     * 선택한 옵션의 순번
26085     */
26086    iOptionIndex: null,
26087
26088    /**
26089     * 선택한 옵션의 옵션 스타일(select : 셀렉트박스, preview : 미리보기, button : 버튼형)
26090     */
26091    sOptionStyle: null,
26092
26093    /**
26094     * 해당 상품 옵션 갯수
26095     */
26096    iOptionCount: 0,
26097
26098    /**
26099     * 품절옵션 표시여부
26100     */
26101    bIsDisplaySolout: true,
26102
26103    /**
26104     * 선택한 옵션의 객체(셀렉트박스 또는 버튼형 옵션 박스(ul태그))
26105     */
26106    oOptionObject: null,
26107
26108    /**
26109     * 선택한 옵션의 다음옵션 Element
26110     */
26111    oNextOptionTarget: null,
26112
26113    /**
26114     * 선택된 옵션 값
26115     */
26116    aOptionValue: [],
26117
26118    /**
26119     * 옵션텍스트에 추가될 항목에대한 정의
26120     */
26121    aExtraOptionText: [
26122        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_PRICE,
26123        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_SOLDOUT,
26124        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_IMAGE,
26125        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM,
26126        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_ITEMSELECTION,
26127        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DIRECT_BASKET,
26128        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_FUNDING,
26129        CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_WMS
26130    ],
26131
26132    /**
26133     * CAFE24.SHOP_FRONT_NEW_OPTION_CONS 객체 Alias
26134     */
26135    cons: null,
26136
26137    /**
26138     * CAFE24.SHOP_FRONT_NEW_OPTION_COMMON 객체 Alias
26139     */
26140    common: null,
26141
26142    /**
26143     * CAFE24.SHOP_FRONT_NEW_OPTION_DATA 객체 Alias
26144     */
26145    data: null,
26146
26147    /**
26148     * CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION 객체 Alias
26149     */
26150    validation: null,
26151
26152    isEnabledOptionInit: function(oOptionChoose)
26153    {
26154        var iProductNum = EC$(oOptionChoose).attr('option_product_no');
26155        //연동형이면서 옵션추가버튼설정이면 순차로딩제외
26156        if (Olnk.isLinkageType(this.common.getOptionType(oOptionChoose)) === true && (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isUseOlnkButton() === true || CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isBindUseOlnkButton(iProductNum) === true)) {
26157            return false;
26158        }
26159
26160        if (this.common.getOptionType(oOptionChoose) === 'F') {
26161            return false;
26162        }
26163
26164        return true;
26165    },
26166
26167    /**
26168     * 각 옵션값에 대한 이벤트 처리
26169     * @param oThis 옵션 셀렉트박스 또는 버튼박스
26170     * @param oSelectedElement 선택한 옵션값
26171     * @param bIsUnset true 이명 deselected된상태로 초기화(setValue를 통해서 틀어왔을떄만 값이 있음)
26172     */
26173    initialize: function(oThis, oSelectedElement, bIsUnset)
26174    {
26175        this.sItemCode = false;
26176        this.oOptionObject = oThis;
26177
26178        // 실제 옵션 처리전에 처리해야할 내용을 모아 놓는다
26179        this.prefetch(oThis);
26180
26181        if (oSelectedElement !== null) {
26182            if (EC$(oSelectedElement).hasClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_DISABLE_CLASS) === true) {
26183                this.setRadioButtonSelect(oSelectedElement, oThis, false);
26184                return;
26185            }
26186
26187            //선택 옵션에대한 disable처리나 활성화 처리
26188            this.setSelectButton(oSelectedElement, bIsUnset);
26189
26190            //필수정보 Set
26191            this.setSelectedOptionConf();
26192
26193            //연동형이면서 옵션추가버튼설정이면 순차로딩제외..
26194            if (this.isEnabledOptionInit(this.oOptionObject) === true) {
26195                var bIsDelete = true;
26196                var bIsRequired = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(this.oOptionObject);
26197                //해당 옵션이 연동형이면서 선택형 옵션이면 하위 옵션은 값만 초기화
26198                if (Olnk.isLinkageType(this.common.getOptionType(this.oOptionObject)) === true && bIsRequired=== false) {
26199                    bIsDelete = false;
26200                }
26201
26202                //선택한 옵션이 옵션이 아닐경우 하위옵션 초기화
26203                //선택한 옵션이 옵션이 아니면 아래 로직은 타지 않고 eachCallback은 실행함
26204                this.setInitializeDefault(this.sOptionGroup, this.iOptionIndex, this.iProductNum, bIsRequired);
26205
26206                if (bIsDelete === true && EC$(oSelectedElement).hasClass(this.cons.BUTTON_OPTION_DISABLE_CLASS) === false && this.validation.isOptionSelected(this.oOptionObject) === true) {
26207                    //선택한 옵션의 다음옵션값을 Parse
26208                    //연동형일경우에는 제외 / 조합분리형만 처리되도록 함
26209                    if (Olnk.isLinkageType(this.sOptionType) === false && this.validation.isSeparateOption(this.oOptionObject) === true) {
26210                        this.data.initializeOptionValue(this.oOptionObject);
26211                    }
26212
26213                    //각 옵션을 초기화및 옵션 리스트 HTML생성
26214                    //조합분리형일때만 처리
26215                    if (this.validation.isSeparateOption(this.oOptionObject) === true) {
26216                        this.setOptionHTML();
26217                    }
26218                }
26219            }
26220
26221            //해당 값이 true나 false이면 setValue를 통해서 들어온것이기때문에 다시 실행할 필요 없음
26222            //if (typeof(bIsUnset) === 'undefined') {
26223                //셀렉트박스 동기화
26224                this.common.setTriggerSelectbox(this.oOptionObject, this.common.getOptionSelectedValue(this.oOptionObject));
26225            //}
26226
26227            //옵션이 모두 선택되었다면 아이템코드를 세팅
26228            this.setItemCode();
26229        }
26230
26231        //옵션선택이 끝나면 각 옵션마다 처리할 프로세스(각 추가기능에서)
26232        this.eachCallback(oThis);
26233
26234        //모든 옵션이 선택되었다면
26235        if (this.sItemCode !== false) {
26236
26237            var sID = this.common.getOptionChooseID(this.oOptionObject);
26238
26239            //상세 메인 상품에서만 실행되도록 예외처리
26240            if (typeof(setPrice) === 'function' && /^product_option_id+/.test(sID) === true) {
26241                setPrice(false, true, sID);
26242            }
26243
26244            //모든 옵션선택이 끝나면 처리할 프로세스(각 추가기능에서)
26245            this.completeCallback(oThis);
26246        }
26247    },
26248
26249    /**
26250     * 실제 옵션의 선택여부를 해제하기전 실행하는 액션
26251     */
26252    prefetch: function(oThis)
26253    {
26254        EC$(this.aExtraOptionText).each(function() {
26255            if (typeof(this.prefetch) === 'function') {
26256                this.prefetch(oThis);
26257            }
26258        });
26259    },
26260
26261    /**
26262     * 각 옵션 선택시마다 처리할 Callback(Extra에 있는 추가기능)
26263     */
26264    eachCallback: function(oThis)
26265    {
26266        EC$(this.aExtraOptionText).each(function() {
26267            if (typeof(this.eachCallback) === 'function') {
26268                this.eachCallback(oThis);
26269            }
26270        });
26271    },
26272
26273    /**
26274     * 옵션선택을 하고 품목이 정해졌을때 Callback(Extra에 있는 추가기능)
26275     */
26276    completeCallback: function(oThis)
26277    {
26278        EC$(this.aExtraOptionText).each(function() {
26279            if (typeof(this.completeCallback) === 'function') {
26280                this.completeCallback(oThis);
26281            }
26282        });
26283    },
26284
26285    /**
26286     * iSelectedOptionSortNum보다 하위 옵션들을 초기상태로 변경함
26287     * @param sOptionGroup 옵션선택박스 그룹
26288     * @param iSelectedOptionSortNum 하위옵션을 초기화할 대상 옵션 순번
26289     * @param iProductNum 상품번호
26290     * @param bIsSetValue COMMON.setValue에서 호출시에는 다시 setValue를 하지 않는다
26291     */
26292    setInitializeDefault: function(sOptionGroup, iSelectedOptionSortNum, iProductNum, bSelectedOptionRequired) {
26293        var iSortNum = 0;
26294        var sHtml = '';
26295        var bIsDelete = null;
26296
26297        EC$('['+this.cons.GROUP_ATTR_NAME+'="'+sOptionGroup+'"]').each(function() {
26298
26299            iSortNum = parseInt(CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getOptionSortNum(this));
26300
26301            //선택한 옵션의 하위옵션들을 초기화
26302            if (iSelectedOptionSortNum < iSortNum) {
26303
26304                var bIsRequired = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(this);
26305
26306                //선택했던 옵션이 연동형이면서 선택형 옵션이면 값만 초기화
26307                //bIsDelete = (bIsDelete = null && isOlnk === true && bSelectedOptionRequired === true && bIsRequired === false) ? false : true;
26308                if (bIsDelete === null) {
26309                    //선택했던 옵션이 선택형 옵션이면 처리하지 않음
26310                    if (bSelectedOptionRequired === false) {
26311                        bIsDelete = false;
26312                    } else if (bSelectedOptionRequired === true) {//선택했던 옵션이 필수옵션이면 진행
26313                        //선택했던 옵션이 필수이면서 현재 옵션이 필수이면 초기화
26314                        if (bIsRequired === true) {
26315                            bIsDelete = true;
26316                        } else {
26317                            //선택했던 옵션이 필수이면서 현재옵션이 선택형옵션이면 다음옵션에서 체크
26318                            bIsDelete = null;
26319                        }
26320                    }
26321                }
26322
26323                if (bIsDelete === true) {
26324                    sHtml = CAFE24.SHOP_FRONT_NEW_OPTION_DATA.getDefaultOptionHTML(iProductNum, iSortNum);
26325                    EC$(this).html('');
26326                    EC$(this).append(sHtml);
26327                }
26328
26329                //셀렉트박스이면서 필수옵션이라면 기본값을 제외하고 option삭제
26330                if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyle(this, CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_SELECT) === true) {
26331
26332                    if (bIsDelete === true && bIsRequired === true) {
26333                        EC$(this).find('option').prop('disabled', false);
26334                        EC$(this).find('option[value!="*"][value!="**"]').remove('option');
26335                    } else {
26336                        CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this, '*', false);
26337                    }
26338                }
26339
26340                if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(this) === true) {
26341                    if (bIsDelete === true && bIsRequired === true) {
26342                        EC$(this).find('li').removeClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_DISABLE_CLASS).addClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_DISABLE_CLASS);
26343                        var oDropDown = EC$(this).closest('.mDropDown');
26344                        oDropDown.addClass("ec-option-disabled").find('.eDropDown > span').html(__('옵션 선택'));
26345                        oDropDown.find('button').attr('disabled', 'disabled');
26346
26347                    } else {
26348                        // 하위 옵션중 선택(div)형
26349                        if (EC$(this).hasClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_DIV_SELECT_CLASS) === true) {
26350                            var oDropDown = EC$(this).closest('.mDropDown');
26351                            oDropDown.find('.eDropDown > span').html(__('옵션 선택'));
26352                            EC$(this).removeClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_SELECTED_CLASS);
26353                        }
26354                    }
26355
26356                    CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this, '*', false);
26357                    //옵션 텍스트 초기화
26358                    CAFE24.SHOP_FRONT_NEW_OPTION_EXTRA_DISPLAYITEM.eachCallback(this);
26359                }
26360
26361                //첫번째 필수 옵션은 그대로 두고 두번째 필수옵션부터 remove
26362                if (bIsDelete !== null && bIsRequired === true) {
26363                    bIsDelete = true;
26364                }
26365            }
26366        });
26367    },
26368
26369    /**
26370     * 옵션이 모두 선택되었다면 아이템코드 Set
26371     */
26372    setItemCode: function() {
26373        //연동형 상품 : 예외적인경우가 많아서 어쩔수가 없음...
26374        if (Olnk.isLinkageType(this.common.getOptionType(this.oOptionObject)) === true) {
26375            //선택한 값이 옵션이 아니라면 false
26376            if (this.validation.isItemCode(this.common.getOptionSelectedValue(this.oOptionObject)) === false) {
26377                return false;
26378            }
26379
26380            //연동형 옵션
26381            var aSelectedValues = this.common.getAllSelectedValue(this.oOptionObject);
26382
26383            //필수옵션 갯수
26384            var iRequiredOption = this.common.getRequiredOption(this.sOptionGroup).length;
26385
26386            //선택한 옵션갯수보다 필수옵션이 많다면 false
26387            if (iRequiredOption > EC$(aSelectedValues).length) {
26388                return false;
26389            }
26390            //실제 필수옵션이 체크되어있는지
26391            var aOptionValues = [];
26392            var bIsExists = false;
26393            var iRequireSelectedOption = 0;
26394
26395            //필수항목만 검사
26396            this.common.getRequiredOption(this.sOptionGroup).each(function() {
26397                bIsExists = false;
26398                aOptionValues = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.getAllOptionValues(this);
26399
26400                //필수 항목 옵션의 값을 실제 선택한옵션가눙데 존재하는지 일일히 확인해야한다
26401                EC$(aSelectedValues).each(function(i, iNo) {
26402                    //선택된 옵션중에 존재한다면 필수값이 선택된것으로 확인
26403                    if (EC$.inArray(iNo, aOptionValues) > -1) {
26404                        bIsExists = true;
26405                        return;
26406                    }
26407                });
26408
26409                if (bIsExists === true) {
26410                    iRequireSelectedOption++;
26411                }
26412            });
26413
26414            //전체 필수값 갯수가 선택된 필수옵션보다 많다면 false
26415            if (iRequiredOption > iRequireSelectedOption) {
26416                return false;
26417            }
26418
26419            this.sItemCode = aSelectedValues;
26420        } else if (this.validation.isSeparateOption(this.oOptionObject) === true) {
26421            //조합분리형은 옵션값으로 파싱해서 가져와야함
26422            if (parseInt(this.iOptionCount) > parseInt(this.aOptionValue.length)) {
26423                return false;
26424            }
26425
26426            this.sItemCode = this.data.getItemCode(this.iProductNum, this.aOptionValue.join(this.cons.OPTION_GLUE));
26427        } else {
26428            //조합분리형 이외에는 선택한 옵션의 value가 아이템코드
26429            this.sItemCode = this.common.getOptionSelectedValue(this.oOptionObject);
26430        }
26431
26432    },
26433
26434    /**
26435     * 각 옵션을 초기화및 옵션 리스트 HTML생성
26436     */
26437    setOptionHTML: function() {
26438        //하위옵션이 없다면(마지막 옵션을 선택한경우) 하위옵션이 없음으로 따로 만들지 않아도 된다
26439        if (parseInt(this.iOptionCount) === parseInt(this.aOptionValue.length)) {
26440            return;
26441        }
26442
26443        if (this.oNextOptionTarget === null) {
26444            return;
26445        }
26446
26447        var sSelectedOption = this.aOptionValue.join(this.cons.OPTION_GLUE);
26448
26449        var aOptions = this.data.getOptionValueArray(this.iProductNum, sSelectedOption);
26450
26451        //셀렉트박스일때 다음옵션 박스 초기화
26452        if (this.common.isOptionStyleButton(this.oNextOptionTarget) === false) {
26453            this.setOptionHtmlForSelect(aOptions, sSelectedOption);
26454        } else {
26455            this.setOptionHtmlForButton(aOptions, sSelectedOption);
26456        }
26457    },
26458
26459    /**
26460     * 버튼형 옵션일 경우 해당 버튼 HTML초기화 및 해당 옵션값 Set
26461     * @param aOptions 옵션값 리스트
26462     * @param sSelectedOption 현재까지 선택된 옵션조합
26463     */
26464    setOptionHtmlForButton: function(aOptions, sSelectedOption) {
26465        //선택한값이 *sk ** 이면 다음옵션을 disable처리
26466        if (this.validation.isItemCode(this.common.getOptionSelectedValue(this.oOptionObject)) === false) {
26467            this.oNextOptionTarget.find('li').removeClass(this.cons.BUTTON_OPTION_DISABLE_CLASS).addClass(this.cons.BUTTON_OPTION_DISABLE_CLASS);
26468        } else {
26469            this.oNextOptionTarget.find('li').removeClass(this.cons.BUTTON_OPTION_DISABLE_CLASS);
26470        }
26471
26472        //연동형일경우에는 disable /  select만 제거
26473        if (Olnk.isLinkageType(this.sOptionType) === true) {
26474            //하위옵션들만 selected클래스 삭제
26475            if (parseInt(EC$(this.oOptionObject).attr('option_sort_no')) < parseInt(EC$(this.oNextOptionTarget).attr('option_sort_no'))) {
26476                EC$(this.oNextOptionTarget).find('li').removeClass(this.cons.BUTTON_OPTION_SELECTED_CLASS);
26477                CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this.oNextOptionTarget, '*', false);
26478
26479                EC$(this.oNextOptionTarget).closest('.mDropDown.ec-option-disabled').removeClass('ec-option-disabled').find('button').removeAttr('disabled');
26480            }
26481            return;
26482        }
26483
26484        this.oNextOptionTarget.find('li').remove('li');
26485
26486        var iNextOptionSortNum = this.common.getOptionSortNum(this.oNextOptionTarget);
26487
26488        var bIsLastOption = false;
26489        //생성될 옵션이 마지막 옵션이면 옵션 Text에 추가 항목(옵션가 품절표시등)을 처리
26490        if (parseInt(iNextOptionSortNum) === this.iOptionCount) {
26491            bIsLastOption = true;
26492        }
26493
26494        var oObject = this;
26495        var sOptionsHtml = '';
26496
26497        //옵션 셀렉트박스 Text에 추가될 문구 처리
26498        var sAddText = '';
26499        var sItemCode = false;
26500        //품절옵션인데 품절옵션표시안함설정이면 삭제
26501        var bIsSoldout = false;
26502        var bIsDisplay = true;
26503
26504        EC$(aOptions).each(function(i, oOption) {
26505            sAddText = '';
26506            bIsSoldout = false;
26507            bIsDisplay = true;
26508            //페이지 로딩시 저장된 해당 옵션의 HTML을 가져온다
26509            sOptionsHtml = oObject.data.getButonOptionHtml(oObject.iProductNum, iNextOptionSortNum, oOption.value);
26510
26511            sOptionsHtml = EC$(sOptionsHtml).clone().removeClass(oObject.cons.BUTTON_OPTION_DISABLE_CLASS);
26512            //마지막 옵션일 경우에는
26513            if (bIsLastOption === true) {
26514                sItemCode = oObject.data.getItemCode(oObject.iProductNum, sSelectedOption + oObject.cons.OPTION_GLUE + oOption.value);
26515
26516                //진열안함이면 패스
26517                if (oObject.common.isDisplay(oObject.iProductNum, sItemCode) === false) {
26518                    bIsDisplay = false;
26519                }
26520
26521                sAddText = oObject.setAddText(oObject.iProductNum, sItemCode);
26522
26523                //품절상품인경우 품절class추가
26524                if (oObject.common.isSoldout(oObject.iProductNum, sItemCode) === true) {
26525                    EC$(sOptionsHtml).removeClass(oObject.cons.BUTTON_OPTION_SOLDOUT_CLASS).addClass(oObject.cons.BUTTON_OPTION_SOLDOUT_CLASS);
26526                    bIsSoldout = true;
26527                }
26528            } else {
26529                var sOptionText = sSelectedOption + oObject.cons.OPTION_GLUE + oOption.value;
26530                sAddText = oObject.common.getSoldoutText(oObject.oNextOptionTarget, sOptionText);
26531
26532                if (sAddText !== '') {
26533                    EC$(sOptionsHtml).addClass(oObject.cons.BUTTON_OPTION_SOLDOUT_CLASS);
26534                    bIsSoldout = true;
26535                }
26536
26537                if (oObject.data.getDisplayFlag(oObject.iProductNum, sOptionText) === false) {
26538                    bIsDisplay = false;
26539                }
26540            }
26541
26542            if ((oObject.bIsDisplaySolout === false && bIsSoldout === true) || bIsDisplay === false) {
26543                EC$(this).remove();
26544                return;
26545            }
26546
26547            if (CAFE24.checkChannelUI() === true) {
26548                oObject.oNextOptionTarget.append(EC$(sOptionsHtml).find('span').html(function(_, oldText) {
26549                    return oldText + sAddText;
26550                }).end().attr('title', oOption.value + sAddText));
26551            } else {
26552                oObject.oNextOptionTarget.append(EC$(sOptionsHtml).attr('title', oOption.value + sAddText));
26553            }
26554        });
26555
26556        oObject.oNextOptionTarget.closest('.mDropDown.ec-option-disabled').removeClass('ec-option-disabled').find('button').removeAttr('disabled')
26557
26558        CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(this.oNextOptionTarget, '*', false);
26559    },
26560
26561    /**
26562     * 셀렉트박스형 옵션일 경우 selectbox초기화 및 해당 옵션값 Set
26563     * @param aOptions 옵션값 리스트
26564     * @param sSelectedOption 현재까지 선택된 옵션조합 배열
26565     */
26566    setOptionHtmlForSelect: function(aOptions, sSelectedOption) {
26567        // 구분선 제외
26568        this.oNextOptionTarget.find('option[value!="**"]').prop('disabled', false);
26569
26570        //연동형일경우에는 초기화 시키고  disable제거
26571        //if (Olnk.isLinkageType(this.sOptionType) === true && CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(this.oNextOptionTarget)) {
26572        if (Olnk.isLinkageType(this.sOptionType) === true) {
26573            var sHtml = this.data.getDefaultOptionHTML(this.common.getOptionProductNum(this.oNextOptionTarget), this.common.getOptionSortNum(this.oNextOptionTarget));
26574            EC$(this.oNextOptionTarget).find('option').remove();
26575            EC$(this.oNextOptionTarget).append(sHtml);
26576            EC$(this.oNextOptionTarget).find('option[value!="**"]').prop('disabled', false);
26577            EC$(this.oNextOptionTarget).val('*');
26578            return;
26579        }
26580
26581        //옵션이 아닌 Default선택값을 제외하고 모두 삭제
26582        this.oNextOptionTarget.find('option[value!="*"][value!="**"]').remove();
26583
26584        //선택한 옵션의 다음순서옵션항목
26585        var iNextOptionSortNum = this.common.getOptionSortNum(this.oNextOptionTarget);
26586
26587        var bIsLastOption = false;
26588        //생성될 옵션이 마지막 옵션이면 옵션 Text에 추가 항목(옵션가 품절표시등)을 처리
26589        if (parseInt(iNextOptionSortNum) === this.iOptionCount) {
26590            bIsLastOption = true;
26591        }
26592
26593        var oObject = this;
26594        var sOptionsHtml = '';
26595
26596        var sItemCode = false;
26597
26598        //옵션 셀렉트박스 Text에 추가될 문구 처리
26599        var sAddText = '';
26600        //품절옵션인데 품절옵션표시안함설정이면 삭제
26601        var bIsSoldout = false;
26602        EC$(aOptions).each(function(i, oOption) {
26603            sAddText = '';
26604            bIsSoldout = false;
26605            bIsDisplay = true;
26606
26607            sOptionsHtml = oObject.data.getButonOptionHtml(oObject.iProductNum, iNextOptionSortNum, oOption.value);
26608            sOptionsHtml = EC$(sOptionsHtml).clone();
26609
26610            //마지막 옵션일 경우에는 설정에따라 옵션title에 추가금액등의 text를 붙인다
26611            if (bIsLastOption === true) {
26612                sItemCode = oObject.data.getItemCode(oObject.iProductNum, sSelectedOption + oObject.cons.OPTION_GLUE + oOption.value);
26613
26614                //진열안함이면 패스
26615                if (oObject.common.isDisplay(oObject.iProductNum, sItemCode) === false) {
26616                    bIsDisplay = false;
26617                }
26618
26619                sAddText = oObject.setAddText(oObject.iProductNum, sItemCode);
26620
26621                bIsSoldout = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isSoldout(oObject.iProductNum, sItemCode);
26622            } else {
26623                //품절문구(각 옵션마다도 보여줘야함...)
26624                var sOptionText = sSelectedOption + oObject.cons.OPTION_GLUE + oOption.value;
26625                sAddText = oObject.common.getSoldoutText(oObject.oNextOptionTarget, sOptionText);
26626                bIsSoldout = (sAddText === '') ? false : true;
26627
26628                if (oObject.data.getDisplayFlag(oObject.iProductNum, sOptionText) === false) {
26629                    bIsDisplay = false;
26630                }
26631            }
26632
26633            if ((oObject.bIsDisplaySolout === false && bIsSoldout === true) || bIsDisplay === false) {
26634                EC$(this).remove();
26635                return;
26636            }
26637
26638            EC$(sOptionsHtml).val(oOption.value);
26639            EC$(sOptionsHtml).prop('disabled', false);
26640            EC$(sOptionsHtml).text(oOption.value + sAddText);
26641
26642            oObject.oNextOptionTarget.append(EC$(sOptionsHtml));
26643        });
26644    },
26645
26646    /**
26647     * 마지막 옵션에 추가될 추가항목들(추가금액, 품절 등)
26648     * @param iProductNum 상품번호
26649     * @param sItemCode 아이템 코드
26650     * @param oOptionElement 옵션셀렉트박스를 임의로 지정할경우
26651     */
26652    setAddText: function(iProductNum, sItemCode, oOptionElement) {
26653        var aText = [];
26654
26655        if (typeof(oOptionElement) !== 'object') {
26656            oOptionElement = this.oOptionObject;
26657        }
26658
26659        EC$(this.aExtraOptionText).each(function() {
26660            if (typeof(this.get) === 'function') {
26661                aText.push(this.get(iProductNum, sItemCode, oOptionElement));
26662            }
26663        });
26664
26665        return aText.join('');
26666    },
26667
26668    /**
26669     * 옵션 선택박스(셀렉트박스나 버튼)에 click 또는 change에 대한 이벤트 할당
26670     */
26671    initChooseBox: function() {
26672        this.cons = CAFE24.SHOP_FRONT_NEW_OPTION_CONS;
26673        this.common = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON;
26674        this.data = CAFE24.SHOP_FRONT_NEW_OPTION_DATA;
26675        this.validation = CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION;
26676
26677        var oThis = this;
26678
26679        //live로 할경우에 기존 이벤트가 없어짐.
26680        EC$('select[option_select_element="ec-option-select-finder"]').off('change focus').change(function() {
26681            if (oThis.common.isOptionStyleButton(this) === true) {
26682                return false;
26683            }
26684
26685            //페이지 로드가 되었는지 확인.
26686            if (typeof(oThis.common.isLoad) === false) {
26687                EC$(this).val('*');
26688                return false;
26689            }
26690
26691            oThis.initialize(this, this);
26692        }).focus(function () {
26693            // select box change 이벤트 발생을 위해, selectedIndex 초기화
26694            // @see ECHOSTING-438245 다중선택형 옵션인 경우에만 동작하도록 조건 추가
26695            if (oSingleSelection.isItemSelectionTypeM() === true && this.selectedIndex > 0) {
26696                this.selectedIndex = 0;
26697            }
26698        });
26699
26700        try {
26701            const sOptionSelector = 'ul[option_select_element="ec-option-select-finder"] > li';
26702            EC$(document).off('click', sOptionSelector).on('click', sOptionSelector, function (e) {
26703                // [ECHOSTING-573085] 버튼형 옵션 내 a 태그 클릭 시 페이지 이동 방지
26704                if (EC$(e.target).closest('a').length > 0) {
26705                    e.preventDefault();
26706                }
26707                var oOptionChoose = EC$(this).parent('ul');
26708
26709                /*
26710                    ECHOSTING-194895 처리를 위해 삭제 (추가 이미지 클릭 시 해당 품목 선택 기능)
26711                    if (e.target.tagName === 'LI') {
26712                        return false;
26713                    }
26714                */
26715
26716                if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(oOptionChoose) === false) {
26717                    return false;
26718                }
26719
26720                //페이지 로드가 되었는지 확인.
26721                if (typeof CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isLoad === false) {
26722                    return false;
26723                }
26724
26725                //라디오버튼일경우 label태그에 상속되기때문에 click이벤트가 label input에 대해 두번 발생함
26726                //라디오버튼 속성이면서 발생위치가 label이면 이벤트 발생하지않고 그냥 return
26727                //return false이면 label클릭시 checked가 안되니깐 그냥 return
26728                //input 태그 자체에 이벤트를 주면 상관없지만 li태그에 이벤트를 할당하기때문에 생기는 현상같음
26729                if (oThis.common.isOptionStyle(oOptionChoose, oThis.cons.OPTION_STYLE_RADIO) === true && e.target.tagName.toUpperCase() === 'LABEL') {
26730                    return;
26731                }
26732
26733                oThis.initialize(EC$(this).parent('ul'), this);
26734            });
26735        } catch (e) {}
26736
26737        try {
26738            EC$('.mDropDown > button').off('click').on('click', function() {
26739                // // 선택옵션 div 셀렉트박스
26740                var oUlElement = EC$(this).siblings('.dropdown').find('ul.' + CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_DIV_SELECT_CLASS + ':not([required])');
26741                if (oUlElement.length > 0) {
26742                    var mDropDownElement = oUlElement.closest('.mDropDown');
26743                    mDropDownElement.find('.eDropDown > span').text(__('옵션 선택'));
26744                    oUlElement.find('li.selected').removeClass('selected ' + CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_SELECTED_CLASS);
26745                    CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.setValue(oUlElement, '*', false);
26746                    oThis.initialize(oUlElement, null);
26747                }
26748            });
26749        } catch (e) {}
26750    },
26751
26752    /**
26753     * 멀팁옵션에서 옵션추가시 이벤트 재정의(버튼형은 live로 되어있으니 상관없고 select형만)
26754     * @param oOptionElement
26755     */
26756    initChooseBoxMulti: function()
26757    {
26758        var oThis = this;
26759
26760        //live로 할경우에 기존 이벤트가 없어짐.
26761        EC$('.xans-product-multioption select[option_select_element="ec-option-select-finder"]').off('change').change(function() {
26762            if (oThis.common.isOptionStyleButton(this) === true) {
26763                return false;
26764            }
26765
26766            //페이지 로드가 되었는지 확인.
26767            if (typeof(oThis.common.isLoad) === false) {
26768                EC$(this).val('*');
26769                return false;
26770            }
26771
26772            oThis.initialize(this, this);
26773        });
26774    },
26775
26776    /**
26777     * 옵션 선택시 필요한 attribute값등을 SET
26778     */
26779    setSelectedOptionConf: function() {
26780        //선택한 옵션 그룹
26781        this.sOptionGroup = this.common.getOptionSelectGroup(this.oOptionObject);
26782
26783        //선택한 옵션값 순번
26784        this.iOptionIndex = parseInt(this.common.getOptionSortNum(this.oOptionObject));
26785
26786        //선택한 옵션 스타일
26787        this.sOptionStyle = EC$(this.oOptionObject).attr(this.cons.OPTION_STYLE);
26788
26789        //현재까지 선택한 옵션의 value값을 가져온다
26790        this.aOptionValue = this.common.getAllSelectedValue(this.oOptionObject);
26791
26792        //상풉번호
26793        this.iProductNum = this.common.getOptionProductNum(this.oOptionObject);
26794
26795        //옵션타입
26796        this.sOptionType = this.common.getOptionType(this.oOptionObject);
26797
26798        //품절 옵션 표시여부
26799        this.bIsDisplaySolout = this.validation.isSoldoutOptionDisplay();
26800
26801        //선택한 옵션의 다음 옵션 Element
26802        //선택옵션을 제거한 다음옵션
26803        //1 : 필수, 2 : 선택, 3 : 필수일때 1번옵션 선택후 다음옵션을 3번(연동형)
26804        //[option_sort_no"'+this.iOptionIndex+'"]
26805        oThis = this;
26806        this.oNextOptionTarget = null;
26807        EC$('[product_option_area="'+this.sOptionGroup+'"][option_product_no="'+this.iProductNum+'"]').each(function() {
26808            //현재선택한 옵션의 하위옵션이 아니라 상위옵션이면 패스
26809            if (oThis.iOptionIndex >= parseInt(oThis.common.getOptionSortNum(this))) {
26810                return true;//continue
26811            }
26812            //선택옵션이면 패스
26813            if (oThis.validation.isRequireOption(this) === false) {
26814                return true;
26815            }
26816
26817            oThis.oNextOptionTarget = EC$(this);
26818            return false;//break
26819        });
26820
26821        //옵션 갯수
26822        this.iOptionCount = EC$('[product_option_area="'+this.sOptionGroup+'"]').length;
26823    },
26824
26825    /**
26826     * 버튼식 옵션일 경우 선택한 옵션을 선택처리
26827     */
26828    setSelectButton: function(oSelectedOption, bIsUnset) {
26829
26830        if (this.common.isOptionStyleButton(this.oOptionObject) === true) {
26831            //모두 선택이 안된상태로 이벤트 실행할수있도록 selected css를 지우고 리턴
26832            if (bIsUnset === true) {
26833                EC$(oSelectedOption).removeClass(this.cons.BUTTON_OPTION_SELECTED_CLASS);
26834                return;
26835            }
26836
26837            //이미 선택한 옵션값을 다시 클릭시에는 선택해제
26838            if (EC$(oSelectedOption).hasClass(this.cons.BUTTON_OPTION_SELECTED_CLASS) === true) {
26839                if (this.common.isOptionStyleDivSelect(this.oOptionObject) === false) {
26840                    EC$(oSelectedOption).removeClass(this.cons.BUTTON_OPTION_SELECTED_CLASS);
26841                    this.common.setValue(this.oOptionObject, '*', false);
26842                    this.setRadioButtonSelect(oSelectedOption, this.oOptionObject, false);
26843                }
26844            } else {
26845                //버튼형식의  옵션일 경우 선택한 옵션을 선택처리(class 명을 추가)
26846                //선택불가일때는 선택된상태로 보이지 않도록 하고 클리만 가능하도록 한다
26847                //disable상태이면 선택CSS는 적용되지 않게 처리
26848                var oTargetOptionElement = EC$(oSelectedOption).parent('ul');
26849                var sDevID = EC$(oTargetOptionElement).attr('ec-dev-id');
26850                var self = this;
26851
26852                //조합일체형에서 구분선이 있을경우 ul태그가 따로있지만 동일옵션이므로
26853                //동일 ul을 구해서 모두 unselect시킨다
26854                EC$(oTargetOptionElement).parent().find('ul[ec-dev-id="'+sDevID+'"]').each(function() {
26855                    EC$(this).find('li').removeClass(self.cons.BUTTON_OPTION_SELECTED_CLASS);
26856                });
26857
26858                EC$(oSelectedOption).addClass(this.cons.BUTTON_OPTION_SELECTED_CLASS);
26859                this.setRadioButtonSelect(oSelectedOption, this.oOptionObject, true);
26860            }
26861        } else {
26862            //셀렉트박스형 옵션일 경우 **를 선택했다면 옵션초기화
26863            if (this.validation.isItemCode(EC$(this.oOptionObject).val()) === false) {
26864                EC$(this.oOptionObject).val('*');
26865            }
26866        }
26867    },
26868
26869    /**
26870     * Disable인 옵션일 경우 체크박스를 다시 해제함
26871     * @param oSelectedOption
26872     * @param oOptionObject
26873     * @param bIsCheck
26874     */
26875    setRadioButtonSelect: function(oSelectedOption, oOptionObject, bIsCheck)
26876    {
26877        if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyle(oOptionObject, CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_RADIO) === false) {
26878            return;
26879        }
26880
26881        EC$(oOptionObject).find('input:radio').prop('checked', false);
26882
26883        //재선택시 체크해제하려면 e107c06faf31 참고
26884        if (bIsCheck === true) {
26885            EC$(oSelectedOption).find('input:radio').prop('checked', true);
26886        }
26887    }
26888};
26889
26890var EC_SHOP_FRONT_NEW_OPTION_BIND = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_BIND');
26891CAFE24.SHOP_FRONT_NEW_OPTION_DATA = {
26892
26893    /**
26894     * CAFE24.SHOP_FRONT_NEW_OPTION_CONS 객체 Alias
26895     */
26896    cons: CAFE24.SHOP_FRONT_NEW_OPTION_CONS,
26897
26898    /**
26899     * CAFE24.SHOP_FRONT_NEW_OPTION_COMMON 객체 Alias
26900     */
26901    common: CAFE24.SHOP_FRONT_NEW_OPTION_COMMON,
26902
26903    /**
26904     * 옵션값관 아이템코드 매칭 데이터(option_value_mapper)
26905     */
26906    aOptioValueMapper: [],
26907
26908    /**
26909     * 각 선택된 옵션값에대한 다음옵션값 리스트를 저장
26910     * aOptionValueData[상품번호][빨강#$%대형] = array(key : 1, value : 옵션값, text : 옵션 Text)
26911     */
26912    aOptionValueData: {},
26913
26914    /**
26915     * 각 상품의 품목데이터(재고 및 추가금액 정보)
26916     */
26917    aItemStockData: {},
26918
26919    /**
26920     * 옵션의 디폴트 HTML을 저장해둠
26921     */
26922    aOptionDefaultData: {},
26923
26924    /**
26925     * 디폴트 옵션을 저장할떄 중복을 제거하기위해서 추가
26926     */
26927    aCacheDefaultProduct: [],
26928
26929    /**
26930     * 버튼형 옵션 Element저장시 중복제거
26931     */
26932    aCacheButtonOption: [],
26933
26934    /**
26935     * 버튼형 옵션의 경우 각 옵션값별 컬러칩/버튼이미지/버튼이름등을 저장해둔다
26936     */
26937    aButtonOptionDefaultData: [],
26938
26939    /**
26940     * 추가금액 노출 설정
26941     */
26942    aOptionPriceDisplayConf: [],
26943
26944    /**
26945     * 연동형 옵션의 옵션내용을 저장
26946     */
26947    aOlnkOptionData: [],
26948
26949    /**
26950     * 각 옵션(품목이 아닌)마다 모두 품절이면 품절표시를 위해서 추가...
26951     */
26952    aOptionSoldoutFlag: [],
26953
26954    /**
26955     * 각 옵션(품목이 아닌)마다 모두 진열안함이면 false로 나오지 않게 하기 위해서 추가
26956     */
26957    aOptionDisplayFlag: [],
26958
26959    /**
26960     * 페이지 로딩시 각 옵션선택박스의 옵션정보를 Parse
26961     */
26962    initData: function() {
26963        var oThis = this;
26964        EC$('select[option_select_element="ec-option-select-finder"], ul[option_select_element="ec-option-select-finder"]').each(function() {
26965            // 중복 로드 시 DOM 요소별 초기화 중복 방지
26966            if (EC$(this).data('option-data-initialized') === true) {
26967                return true;
26968            }
26969            EC$(this).data('option-data-initialized', true);
26970
26971            //해당 옵션의 상품번호
26972            var iProductNum = oThis.common.getOptionProductNum(this);
26973            //해당 옵션의 옵션순서번호
26974            var iOptionSortNum = oThis.common.getOptionSortNum(this);
26975
26976            var sCacheKey = iProductNum + oThis.cons.OPTION_GLUE + iOptionSortNum;
26977
26978            CAFE24.SHOP_FRONT_NEW_OPTION_DATA.initializeOption(this, sCacheKey);
26979
26980            //버튼형 옵션일 경우 각 Element를 캐싱
26981            if (CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isOptionStyleButton(this) === true) {
26982                CAFE24.SHOP_FRONT_NEW_OPTION_DATA.initializeOptionForButtonOption(this, sCacheKey);
26983            } else {
26984                CAFE24.SHOP_FRONT_NEW_OPTION_DATA.initializeOptionForSelectOption(this, sCacheKey);
26985                //일반 셀렉트의 경우 기본값 (*, **)을 제외하고 삭제
26986                //첫번째 필수값은 option들이 disable이 아니므로 disable된 옵션들만 삭제
26987                var bIsProcLoading = true;
26988
26989                //필수옵션만 삭제
26990                if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(this) === false) {
26991                    bIsProcLoading = false;
26992                }
26993
26994                //disable만 풀어준다
26995                //연동형이지만 옵션추가버튼 사용시에는 지우지 않음...
26996                //기본으로 선택된값이 있다면 지우지 않음(구스킨 관심상품, 뉴스킨 장바구니등에서는 일단 선택한 옵션을 보여주고 선택후부터 순차로딩)
26997                var sValue = EC$(this).find('option[selected="selected"]').val();
26998                if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isItemCode(sValue) === true || (Olnk.isLinkageType(oThis.common.getOptionType(this)) === true && (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isUseOlnkButton() === true || CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isBindUseOlnkButton(iProductNum) === true))) {
26999                    bIsProcLoading = false;
27000                    EC$(this).find('option[value!="**"]').prop('disabled', false);
27001                }
27002
27003                if (bIsProcLoading === true) {
27004                    EC$(this).find('option[value!="*"][value!="**"]:disabled').remove('option');
27005                }
27006            }
27007        });
27008    },
27009
27010    /**
27011     * 각 상품의 옵션 디폴트 옵션 HTML을 저장해둔다
27012     * @param oOptionChoose 값을 가져오려는 옵션박스 object
27013     */
27014    initializeOption: function(oOptionChoose, sCacheKey) {
27015        //이미 데이터가 있다면 패스
27016        if (EC$.inArray(sCacheKey, this.aCacheDefaultProduct) > -1) {
27017            return;
27018        }
27019
27020        this.aCacheDefaultProduct.push(sCacheKey);
27021        this.aOptionDefaultData[sCacheKey] = EC$(oOptionChoose).html();
27022    },
27023
27024    initializeOptionForSelectOption: function(oOptionChoose, sCacheKey) {
27025        var iProductNum = EC$(oOptionChoose).attr('option_product_no');
27026        var oThis = this;
27027        //같은 상품이 여러개있을수있으므로 이미 캐싱이 안된 상품만
27028        if (EC$.inArray(sCacheKey, this.aCacheButtonOption) < 0) {
27029            var bDisabled = false;
27030            if (Olnk.isLinkageType(this.common.getOptionType(oOptionChoose)) === true && (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isUseOlnkButton() === true || CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isBindUseOlnkButton(iProductNum) === true)) {
27031                bDisabled = true;
27032            }
27033
27034            this.aCacheButtonOption.push(sCacheKey);
27035            this.aButtonOptionDefaultData[sCacheKey] = [];
27036
27037            EC$(oOptionChoose).find('option').each(function() {
27038                if (bDisabled === true && this.value !== '**') {
27039                    EC$(this).prop('disabled', false);
27040                }
27041                oThis.aButtonOptionDefaultData[sCacheKey][EC$(this).val()] = EC$('<div>').append(EC$(this).clone()).html();
27042            });
27043        }
27044    },
27045
27046    /**
27047     * 셀렉트박스 형식이 아닌 버튼이나 이미지형 옵션일 경우 HTML자체를 옵션값 별로 저장해둔다.
27048     * writejs쓰기싫음여
27049     */
27050    initializeOptionForButtonOption: function(oOptionChoose, sCacheKey) {
27051        var oThis = this;
27052        var iProductNum = EC$(oOptionChoose).attr('option_product_no');
27053
27054        //같은 상품이 여러개있을수있으므로 이미 캐싱이 안된 상품만
27055        if (EC$.inArray(sCacheKey, this.aCacheButtonOption) < 0) {
27056            var bDisabled = false;
27057            if (Olnk.isLinkageType(this.common.getOptionType(oOptionChoose)) === true && (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isUseOlnkButton() === true || CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isBindUseOlnkButton(iProductNum) === true)) {
27058                bDisabled = true;
27059            }
27060
27061            this.aCacheButtonOption.push(sCacheKey);
27062            this.aButtonOptionDefaultData[sCacheKey] = [];
27063
27064            EC$(oOptionChoose).find('li').each(function() {
27065                if (bDisabled === true) {
27066                    EC$(this).removeClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.BUTTON_OPTION_DISABLE_CLASS);
27067                }
27068                oThis.aButtonOptionDefaultData[sCacheKey][EC$(this).attr('option_value')] = EC$('<div>').append(EC$(this).clone()).html();
27069            });
27070        }
27071
27072        //조합일체형인 경우 기존생성된 셀렉트박스는 삭제
27073        if (EC$(oOptionChoose).attr('item_listing_type') === 'C') {
27074            EC$('select[product_option_area_select="addproduct_option_' + iProductNum + '"]').remove();
27075        }
27076
27077        var oTriggerSelect = this.getSelectClone(oOptionChoose);
27078
27079        oTriggerSelect.append(EC$('<option>').val('*').text('empty'));
27080
27081        var sTitle = '';
27082        var sValue = '';
27083        for (x in this.aButtonOptionDefaultData[sCacheKey]) {
27084            //IE8..
27085            if (x !== 'indexOf') {
27086                sTitle = EC$(oThis.aButtonOptionDefaultData[sCacheKey][x]).attr('title');
27087                sValue = EC$(oThis.aButtonOptionDefaultData[sCacheKey][x]).attr('option_value');
27088
27089                oTriggerSelect.append(EC$('<option>').val(sValue).text(sTitle));
27090            }
27091        }
27092
27093        oTriggerSelect.val('*');
27094        EC$(oOptionChoose).parent().append(oTriggerSelect);
27095    },
27096    /**
27097     * 옵션 선택 UI의 미러링 객체 생성
27098     * @param oOptionChoose
27099     * @returns {jQuery}
27100     */
27101    getSelectClone: function(oOptionChoose)
27102    {
27103        var aAttribute = {
27104            'product_option_area_select': EC$(oOptionChoose).attr('product_option_area'),
27105            'id': EC$(oOptionChoose).attr('ec-dev-id'),
27106            'name': EC$(oOptionChoose).attr('ec-dev-name'),
27107            'option_title': EC$(oOptionChoose).attr('option_title'),
27108            'option_type': EC$(oOptionChoose).attr('option_type'),
27109            'item_listing_type': EC$(oOptionChoose).attr('item_listing_type'),
27110            'composition-code': EC$(oOptionChoose).attr('composition-code'),
27111            'option_code': EC$(oOptionChoose).attr('option_code')
27112        };
27113        // 셀렉트 박스의 셀렉터가 ^=라서 클래스의 순서가 중요
27114        var aClass = [];
27115        if (typeof(EC$(oOptionChoose).attr('ec-dev-class')) !== 'undefined') {
27116            aClass.push(EC$(oOptionChoose).attr('ec-dev-class'));
27117        }
27118        aClass.push('displaynone');
27119
27120        if (EC$(oOptionChoose).hasClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_DIV_SELECT_CLASS) === true) {
27121            aClass.push(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_STYLE_DIV_SELECT_CLASS);
27122        }
27123
27124        if (EC$(oOptionChoose).hasClass(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_SET_PRODUCT_NONE_CLASS) === true) {
27125            aClass.push(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_SET_PRODUCT_NONE_CLASS);
27126        }
27127
27128        var oReturn = EC$('<select required="true">').attr(aAttribute).addClass(aClass.join(' '));
27129        if (CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION.isRequireOption(oOptionChoose) === false) {
27130            // true/false를 string으로 지정해야해서 먼저 string으로 지정해주고 필요없으면 제거
27131            oReturn.removeAttr('required');
27132        }
27133        return oReturn;
27134    },
27135
27136    /**
27137     * 버튼형 옵션의 상품 옵션값에 대한 옵션 HTML을 반환
27138     * @param iProductNum 상품번호
27139     * @param iOptionSortNum 옵션순서
27140     * @param sOptionValue 옵션값
27141     * @returns boolean 해당 옵션값에 대한 버튼 HTML
27142     */
27143    getButonOptionHtml: function(iProductNum, iOptionSortNum, sOptionValue) {
27144        var sCacheKey = iProductNum + this.cons.OPTION_GLUE + iOptionSortNum;
27145
27146        //없을경우에는 다시 초기화
27147        if (typeof(this.aButtonOptionDefaultData[sCacheKey]) === 'undefined') {
27148            this.initData();
27149        }
27150
27151        if (typeof(this.aButtonOptionDefaultData[sCacheKey][sOptionValue]) === 'undefined') {
27152            return false;
27153        }
27154
27155        return this.aButtonOptionDefaultData[sCacheKey][sOptionValue];
27156    },
27157
27158    /**
27159     * 옵션을 선택하지 않았을때 하위옵션을 초기화하기위해서 디폴트 HTML을 가져옴
27160     * @param iProductNum 상품번호
27161     * @param iOptionSortNum 옵션 순서
27162     */
27163    getDefaultOptionHTML: function(iProductNum, iOptionSortNum)
27164    {
27165        var sCacheKey = iProductNum + this.cons.OPTION_GLUE + iOptionSortNum;
27166
27167        if (typeof(this.aOptionDefaultData[sCacheKey]) === 'undefined') {
27168            return;
27169        }
27170
27171        return this.aOptionDefaultData[sCacheKey];
27172    },
27173
27174    /**
27175     * 해당 상품의 옵션 재고 관련 데이터를 리턴
27176     * @param iProductNum 상품번호
27177     */
27178    getProductStockData: function(iProductNum) {
27179        if (typeof(this.aItemStockData[iProductNum]) === 'undefined') {
27180            try {
27181                var rawData = window["option_stock_data" + iProductNum];
27182                if (rawData !== undefined) {
27183                    this.aItemStockData[iProductNum] = CAFE24.UTIL.parseJSON(rawData);
27184                }
27185            } catch (e) {
27186                console.error('옵션 데이터를 불러오는 데 실패했습니다: ' + e.message);
27187            }
27188        }
27189
27190        if (this.aItemStockData.hasOwnProperty(iProductNum) === false) {
27191            return null;
27192        }
27193
27194        return this.aItemStockData[iProductNum];
27195    },
27196
27197    /**
27198     * 옵션이 모두 선택되었다면 옵션값 리턴
27199     * @param iProductNum 상품번호
27200     * @param sSelectedOptionValue 선택된 전체 옵션값
27201     * @returns boolean 아이템코드
27202     */
27203    getItemCode: function(iProductNum, sSelectedOptionValue) {
27204        if (typeof(this.aOptioValueMapper[iProductNum]) === 'undefined') {
27205            return false;
27206        }
27207
27208        if (typeof(this.aOptioValueMapper[iProductNum][sSelectedOptionValue]) === 'undefined') {
27209            return false;
27210        }
27211
27212        return this.aOptioValueMapper[iProductNum][sSelectedOptionValue];
27213    },
27214
27215    /**
27216     * 해당 상품의 선택된 옵션의 하위 옵션을 리턴
27217     * @param iProductNum 상품번호
27218     * @param sSelectedValue 현재까지 선택된 옵션값 String(옵션1값 + EC_SHOP_FRONT_NEW_OPTION_CONS.OPTION_GLUE + 옵션2값 형식)
27219     * @returns 옵션리스트
27220     */
27221    getOptionValueArray: function(iProductNum, sSelectedValue) {
27222        if (typeof(this.aOptionValueData[iProductNum]) === 'undefined') {
27223            return false;
27224        }
27225
27226        if (typeof(this.aOptionValueData[iProductNum][sSelectedValue]) === 'undefined') {
27227            return false;
27228        }
27229
27230        return this.aOptionValueData[iProductNum][sSelectedValue];
27231    },
27232
27233    /**
27234     * 옵션 생성에 필요한 기본데이터 정의
27235     */
27236    setDefaultData: function() {
27237        if (typeof(option_stock_data) !== 'undefined') {
27238            this.aItemStockData[iProductNo] = CAFE24.UTIL.parseJSON(option_stock_data);
27239        }
27240        if (typeof(option_value_mapper) !== 'undefined') {
27241            this.aOptioValueMapper[iProductNo] = CAFE24.UTIL.parseJSON(option_value_mapper);
27242        }
27243        if (typeof(product_option_price_display) !== 'undefined') {
27244            this.aOptionPriceDisplayConf[iProductNo] = product_option_price_display;
27245        }
27246
27247        if (typeof(add_option_data) !== 'undefined') {
27248            var aAddOptionJson = CAFE24.UTIL.parseJSON(add_option_data);
27249            for (var iAddProductNo in aAddOptionJson) {
27250                this.aItemStockData[iAddProductNo] = CAFE24.UTIL.parseJSON(aAddOptionJson[iAddProductNo].option_stock_data);
27251                if (typeof(aAddOptionJson[iAddProductNo].option_value_mapper) !== 'undefined') {
27252                    this.aOptioValueMapper[iAddProductNo] = CAFE24.UTIL.parseJSON(aAddOptionJson[iAddProductNo].option_value_mapper);
27253                }
27254
27255                this.aOptionPriceDisplayConf[iAddProductNo] = aAddOptionJson[iAddProductNo].product_option_price_display;
27256            }
27257        }
27258
27259        if (typeof(set_option_data) !== 'undefined') {
27260            var aSetProductData = CAFE24.UTIL.parseJSON(set_option_data);
27261            for (var iSetProductNo in aSetProductData) {
27262                this.aItemStockData[iSetProductNo] = CAFE24.UTIL.parseJSON(aSetProductData[iSetProductNo].option_stock_data);
27263
27264                if (typeof(aSetProductData[iSetProductNo].option_value_mapper) !== 'undefined') {
27265                    this.aOptioValueMapper[iSetProductNo] = CAFE24.UTIL.parseJSON(aSetProductData[iSetProductNo].option_value_mapper);
27266                }
27267
27268                this.aOptionPriceDisplayConf[iSetProductNo] = aSetProductData[iSetProductNo].product_option_price_display;
27269            }
27270        }
27271    },
27272
27273    /**
27274     * 이벤트 옵션의 다음옵션값을 세팅
27275     * @param oOptionChoose 값을 가져오려는 옵션박스 object
27276     */
27277    initializeOptionValue: function(oOptionChoose) {
27278        //상품번호
27279        var iProductNum = this.common.getOptionProductNum(oOptionChoose);
27280
27281        //현재까지 선택된 옵션값 배열
27282        var aSelectedValue = this.common.getAllSelectedValue(oOptionChoose);
27283
27284        var sSelectedValue = aSelectedValue.join(this.cons.OPTION_GLUE);
27285
27286        //기존 선언되지 않은 옵션에대한 처리면 뱌열로 미리 선언
27287        //이미 옵션값이 set되어있으면 바로 리턴
27288        if (typeof(this.aOptionValueData[iProductNum]) === 'undefined') {
27289            this.aOptionValueData[iProductNum] = {};
27290        }
27291        if (typeof(this.aOptionValueData[iProductNum][sSelectedValue]) === 'undefined') {
27292            this.aOptionValueData[iProductNum][sSelectedValue] = new Array();
27293        } else {
27294            return;
27295        }
27296
27297        //선택한 옵션의 순번
27298        var iOptionSortNum = this.common.getOptionSortNum(oOptionChoose);
27299
27300        //옵션값 순서
27301        var iCnt = 1;
27302        //중복옵션값 제거하기 위해서 저장할 옵션값
27303        var aCheckDuplicate = [];
27304
27305
27306        //장바구니 관심상품쪽은 데이터가 이렇게되어있어서 페이지로드시에 어떻게 할수가 없네요..
27307        if (typeof(this.aOptioValueMapper[iProductNum]) === 'undefined') {
27308            try {
27309                var rawData = window["option_value_mapper" + iProductNum];
27310                if (rawData !== undefined) {
27311                    this.aOptioValueMapper[iProductNum] = CAFE24.UTIL.parseJSON(rawData);
27312                }
27313            } catch (e) {
27314                console.error('옵션 데이터를 불러오는 데 실패했습니다: ' + e.message);
27315            }
27316        }
27317
27318        for (var x in this.aOptioValueMapper[iProductNum]) {
27319
27320            //옵션값을 구분자에 따라 배열로 분리(옵션값 => 아이템코드 형태
27321            var aOptions = x.split(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_GLUE);
27322
27323            //옵션값에서 기선택된 값과 비교하기위한 옵션값
27324            var sOptionValue = aOptions.splice(0, iOptionSortNum).join(this.cons.OPTION_GLUE);
27325
27326            //첫번째옵션부터 마지막선택한 옵션까지의 옵션값이 똑같으면서 기존처리된 옵션값이 아니라면 배열에 저장
27327            if (String(sOptionValue) === String(sSelectedValue) && EC$.inArray(aOptions[0], aCheckDuplicate) < 0) {
27328                this.aOptionValueData[iProductNum][sSelectedValue].push({key: iCnt, value: aOptions[0]});
27329                iCnt++;
27330                aCheckDuplicate.push(aOptions[0]);
27331            }
27332        }
27333    },
27334
27335    /**
27336     * 각 옵션값의 전체품절 여부
27337     * @param iProductNum 상품번호
27338     * @param sValue 옵션값
27339     * @returns
27340     */
27341    getSoldoutFlag: function(iProductNum, sValue) {
27342        if (typeof(this.aOptionSoldoutFlag[iProductNum][sValue]) === 'undefined') {
27343            return false;
27344        }
27345
27346        return this.aOptionSoldoutFlag[iProductNum][sValue];
27347    },
27348
27349    /**
27350     * 각 옵션값의 진열 여부
27351     * @param iProductNum 상품번호
27352     * @param sValue 옵션값
27353     * @returns
27354     */
27355    getDisplayFlag: function(iProductNum, sValue) {
27356
27357        if (typeof(this.aOptionDisplayFlag[iProductNum][sValue]) === 'undefined') {
27358            return false;
27359        }
27360
27361        return this.aOptionDisplayFlag[iProductNum][sValue];
27362    },
27363
27364    /**
27365     * 각각의 옵션값(품목말고)마다 해당 옵션전체가 품절인지 체크...
27366     * @param oOptionChoose
27367     */
27368    initializeSoldoutFlag: function(oOptionChoose) {
27369        //해당 옵션의 상품번호
27370        var iProductNum = this.common.getOptionProductNum(oOptionChoose);
27371
27372        if (typeof(this.aOptionSoldoutFlag[iProductNum]) === 'undefined') {
27373            this.aOptionSoldoutFlag[iProductNum] = [];
27374        }
27375
27376        if (typeof(this.aOptionDisplayFlag[iProductNum]) === 'undefined') {
27377            this.aOptionDisplayFlag[iProductNum] = [];
27378        }
27379
27380        //장바구니 관심상품쪽은 데이터가 이렇게되어있어서 페이지로드시에 어떻게 할수가 없네요..
27381        if (typeof(this.aOptioValueMapper[iProductNum]) === 'undefined') {
27382            try {
27383                var rawData = window["option_value_mapper" + iProductNum];
27384                if (rawData !== undefined) {
27385                    this.aOptioValueMapper[iProductNum] = CAFE24.UTIL.parseJSON(rawData);
27386                }
27387            } catch (e) {
27388                console.error('옵션 데이터를 불러오는 데 실패했습니다: ' + e.message);
27389            }
27390        }
27391
27392        for (var x in this.aOptioValueMapper[iProductNum]) {
27393            //옵션값을 구분자에 따라 배열로 분리(옵션값 => 아이템코드 형태
27394            var aOptions = x.split(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_GLUE);
27395
27396            var bIsSoldout = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isSoldout(iProductNum, this.aOptioValueMapper[iProductNum][x]);
27397
27398            var bIsDisplay = CAFE24.SHOP_FRONT_NEW_OPTION_COMMON.isDisplay(iProductNum, this.aOptioValueMapper[iProductNum][x]);
27399
27400            for (var i = 1; i <= EC$(aOptions).length; i++) {
27401                var sOption = aOptions.slice(0, i).join(CAFE24.SHOP_FRONT_NEW_OPTION_CONS.OPTION_GLUE);
27402
27403                //일단 품절로 세팅하고 품절이 아닌게 하나라도있다면 false로 바꿔준다
27404                if (typeof(this.aOptionSoldoutFlag[iProductNum][sOption]) === 'undefined') {
27405                    this.aOptionSoldoutFlag[iProductNum][sOption] = true;
27406                }
27407
27408                if (bIsSoldout === false) {
27409                    this.aOptionSoldoutFlag[iProductNum][sOption] = false;
27410                }
27411
27412                //일단 진열안함으로 세팅후에 한개라도 진열함이있다면 true바꿔줌다
27413                if (typeof(this.aOptionSoldoutFlag[iProductNum][sOption]) === 'undefined') {
27414                    this.aOptionDisplayFlag[iProductNum][sOption] = false;
27415                }
27416
27417                if (bIsDisplay === true) {
27418                    this.aOptionDisplayFlag[iProductNum][sOption] = true;
27419                }
27420            }
27421        }
27422    }
27423};
27424
27425var EC_SHOP_FRONT_NEW_OPTION_DATA = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_DATA');
27426
27427CAFE24.SHOP_FRONT_NEW_OPTION_VALIDATION = {
27428    /**
27429     * CAFE24.SHOP_FRONT_NEW_OPTION_COMMON Obejct Alias
27430     */
27431    common: CAFE24.SHOP_FRONT_NEW_OPTION_COMMON,
27432
27433    cons: CAFE24.SHOP_FRONT_NEW_OPTION_CONS,
27434
27435    /**
27436     * 해당 옵션 그룹에 필수옵션이 속해있는지 여부 확인
27437     * @param sOptionGroup 옵션 그룹 (@see : CAFE24.SHOP_FRONT_NEW_OPTION_GROUP_CONS)
27438     * @returns 필수옵션 존재 여부
27439     */
27440    checkRequiredOption: function(sOptionGroup) {
27441        //해당 옵션 그룹의 필수옵션 갯수
27442        var iRequiredOption = EC$(this.common.getRequiredOption(sOptionGroup)).length;
27443
27444        return (parseInt(iRequiredOption) > 0) ? true : false;
27445    },
27446
27447    /**
27448     * 해당 옵션이 필수옵션인지 확인
27449     * @param oOptionChoose 값을 가져오려는 옵션박스 object
27450     */
27451    isRequireOption: function(oOptionChoose) {
27452        return (Boolean(EC$(oOptionChoose).attr('required')) === true);
27453    },
27454
27455    /**
27456     * 해당 값이 아이템코드인지 확인
27457     * @param sItemCode 선택한 아이템코드
27458     * @returns true이면 아이템코드
27459     * @todo 아이템코드 정규식을 추가..해야하나?? 그래야한다면 선택값여부를(*, **) 따로두고 실제 아이템코드인지 여부를 더 확인해야함
27460     */
27461    isItemCode: function(sItemCode) {
27462        return (EC$.inArray(sItemCode, ['*', '**']) > -1 || typeof(sItemCode) === 'undefined') ? false : true;
27463    },
27464
27465    /**
27466     * 옵션값이 선택되어있는지 확인
27467     * @param oOptionChoose 값을 가져오려는 옵션박스 object
27468     */
27469    isOptionSelected: function(oOptionChoose) {
27470        return (EC$.inArray(this.common.getOptionSelectedValue(oOptionChoose), ['*', '**']) > -1) ? false : true;
27471    },
27472    
27473    /**
27474     * 옵션그룹에서 하나라도 선택이 되었는지 확인
27475     */
27476    isOptionGroupSelected: function(sOptionGroup)
27477    {
27478        var oThis = this;
27479        var bIsChoosen = false;
27480        EC$('[' + this.cons.GROUP_ATTR_NAME + '^="' + sOptionGroup + '"]').each(function() {
27481            if (oThis.isOptionSelected(this) === true) {
27482                bIsChoosen = true;
27483                return false;
27484            }
27485        });
27486        return bIsChoosen;
27487    },
27488    
27489    /**
27490     * 필수 옵션이 모두 선택된 상태인지 여부 확인
27491     * @param sOptionGroup 선택한 아이템코드
27492     * @returns boolean true이면 아이템코드
27493     */
27494    isSelectedRequiredOption: function(sOptionGroup) {
27495        //필수옵션이 하나도 없다면 바로 true
27496        if (this.checkRequiredOption(sOptionGroup) === false) {
27497            return true;
27498        }
27499
27500        var oThis = this;
27501        var bIsComplete = true;
27502        EC$('[' + this.cons.GROUP_ATTR_NAME + '^="' + sOptionGroup + '"]').each(function() {
27503
27504            //핑수옵션이지만 값이 선택되지 않았을경우 false
27505            if (oThis.isRequireOption(this) === true && oThis.isOptionSelected(this) === false) {
27506                bIsComplete = false;
27507                return false;
27508            }
27509        });
27510
27511        return bIsComplete;
27512    },
27513
27514    /**
27515     * 조합분리형만 아이템코드를 가져오는방식이 틀려서 확인용을 추가(연동형도 일단 조합분리형으로 인식하도록 함)
27516     * @param oOptionChoose 구분할 옵션박스 object
27517     * @returns true => 조합분리형, false => 기타옵션타입
27518     */
27519    isSeparateOption: function(oOptionChoose) {
27520        var sOptionTypeStr = EC$(oOptionChoose).attr('option_type');
27521        var sOptionListStr = EC$(oOptionChoose).attr('item_listing_type');
27522        return (Olnk.isLinkageType(sOptionTypeStr) === true || (sOptionTypeStr === 'T' && sOptionListStr === 'S')) ? true : false;
27523    },
27524
27525    /**
27526     * 연동형 옵션 추가 버튼 사용설정을 사용하면 또 순차로딩 하지 않음
27527     * @returns
27528     */
27529    isUseOlnkButton: function() {
27530        return Olnk.getOptionPushbutton(EC$('#option_push_button'));
27531    },
27532    /**
27533     * 세트상품에서 연동형 옵션 추가 버튼 사용설정을 사용하면 또 순차로딩 하지 않음
27534     * @returns
27535     */
27536    isBindUseOlnkButton: function(iProductNum) {
27537        return EC$('#add_option_push_button_'+iProductNum).length > 0;
27538    },
27539    isSoldoutOptionDisplay: function() {
27540        return (typeof(bIsDisplaySoldoutOption) !== 'undefined') ? bIsDisplaySoldoutOption : true;
27541    }
27542};
27543
27544var EC_SHOP_FRONT_NEW_OPTION_VALIDATION = CAFE24.getDeprecatedNamespace('EC_SHOP_FRONT_NEW_OPTION_VALIDATION');
27545
27546/**
27547 * 쇼핑몰 금액 라이브러리
27548 */
27549CAFE24.SHOP_PRICE = {
27550
27551    /**
27552     * iShopNo 쇼핑몰의 결제화폐에 맞게 리턴합니다.
27553     * @param float fPrice 금액
27554     * @param bool bIsNumberFormat number_format 적용 유무
27555     * @param int iShopNo 쇼핑몰번호
27556     * @return float|string
27557     */
27558    toShopPrice: function(fPrice, bIsNumberFormat, iShopNo)
27559    {
27560        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27561
27562        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27563            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[undefined];
27564        } else {
27565            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo];
27566        }
27567
27568        // 쇼핑몰 결제화폐 정보 사용 (DB/Redis 저장용, Presentment 환율 미적용)
27569        var aCurrencyInfo = oCurrencyInfo.aShopCurrencyInfo;
27570
27571        return CAFE24.SHOP_PRICE.toPrice(fPrice, aCurrencyInfo, bIsNumberFormat);
27572    },
27573
27574    /**
27575     * iShopNo 쇼핑몰의 Presentment 화폐에 맞게 리턴합니다. (화면 표시용)
27576     * Global-e Presentment 활성 시 환율 적용 + Presentment 화폐 정보 사용,
27577     * 비활성 시 기본 결제화폐 정보 사용 (toShopPrice와 동일 동작)
27578     * @param float fPrice 금액
27579     * @param bool bIsNumberFormat number_format 적용 유무
27580     * @param int iShopNo 쇼핑몰번호
27581     * @return float|string
27582     */
27583    toShopPricePresentment: function(fPrice, bIsNumberFormat, iShopNo)
27584    {
27585        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27586
27587        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27588            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[undefined];
27589        } else {
27590            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo];
27591        }
27592
27593        if (oCurrencyInfo.bPresentmentEnabled === true) {
27594            // Global-e Presentment: 환율 적용 + Presentment 화폐 정보 사용
27595            fPrice = fPrice * (oCurrencyInfo.fExchangePresentmentRate || 0);
27596            var aCurrencyInfo = oCurrencyInfo.aPresentmentCurrencyInfo;
27597        } else {
27598            // 기본: 쇼핑몰 결제화폐 정보 사용
27599            var aCurrencyInfo = oCurrencyInfo.aShopCurrencyInfo;
27600        }
27601
27602        return CAFE24.SHOP_PRICE.toPrice(fPrice, aCurrencyInfo, bIsNumberFormat);
27603    },
27604
27605    /**
27606     * iShopNo 쇼핑몰의 참조화폐에 맞게 리턴합니다.
27607     * @param float fPrice 금액
27608     * @param bool bIsNumberFormat number_format 적용 유무
27609     * @param int iShopNo 쇼핑몰번호
27610     * @return float|string
27611     */
27612    toShopSubPrice: function(fPrice, bIsNumberFormat, iShopNo)
27613    {
27614        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27615
27616        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27617            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[undefined];
27618        } else {
27619            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo];
27620        }
27621
27622        // Presentment 활성 시 참조화폐 표시 비활성화
27623        if (oCurrencyInfo.bPresentmentEnabled === true) {
27624            return '';
27625        }
27626
27627        // 참조화폐 정보
27628        var aSubCurrencyInfo = oCurrencyInfo.aShopSubCurrencyInfo;
27629
27630        if (! aSubCurrencyInfo) {
27631            // 참조화폐가 없으면
27632            return '';
27633
27634        } else {
27635            var aCurrencyInfo = oCurrencyInfo.aShopCurrencyInfo;
27636
27637            if (aSubCurrencyInfo.currency_code === aCurrencyInfo.currency_code) {
27638                // 결제화폐와 참조화폐가 동일하면
27639                return '';
27640            } else {
27641                return CAFE24.SHOP_PRICE.toPrice(fPrice, aSubCurrencyInfo, bIsNumberFormat);
27642            }
27643        }
27644    },
27645
27646    /**
27647     * 쇼핑몰의 기준화폐에 맞게 리턴합니다.
27648     * @param float fPrice 금액
27649     * @param bool bIsNumberFormat number_format 적용 유무
27650     * @param int iShopNo 쇼핑몰번호
27651     * @return float
27652     */
27653    toBasePrice: function(fPrice, bIsNumberFormat, iShopNo)
27654    {
27655        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27656
27657        // 기준화폐 정보
27658        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27659            var aBaseCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[undefined].aBaseCurrencyInfo;
27660        } else {
27661            var aBaseCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aBaseCurrencyInfo;
27662        }
27663
27664        return CAFE24.SHOP_PRICE.toPrice(fPrice, aBaseCurrencyInfo, bIsNumberFormat);
27665    },
27666
27667    /**
27668     * 결제화폐 금액을 참조화폐 금액으로 변환하여 리턴합니다.
27669     * @param float fPrice 금액
27670     * @param bool bIsNumberFormat number_format 적용 유무
27671     * @param int iShopNo 쇼핑몰번호
27672     * @return float 참조화폐 금액
27673     */
27674    shopPriceToSubPrice: function(fPrice, bIsNumberFormat, iShopNo)
27675    {
27676        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27677
27678        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27679            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[undefined];
27680        } else {
27681            var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo];
27682        }
27683
27684        // Presentment 활성 시 참조화폐 표시 비활성화
27685        if (oCurrencyInfo.bPresentmentEnabled === true) {
27686            return '';
27687        }
27688
27689        // 결제화폐 금액 => 참조화폐 금액
27690        fPrice = fPrice * (oCurrencyInfo.fExchangeSubRate || 0);
27691
27692        return CAFE24.SHOP_PRICE.toShopSubPrice(fPrice, bIsNumberFormat, iShopNo);
27693    },
27694
27695    /**
27696     * 결제화폐 대비 기준화폐 환율 리턴
27697     * @param int iShopNo 쇼핑몰번호
27698     * @return float 결제화폐 대비 기준화폐 환율
27699     */
27700    getRate: function(iShopNo)
27701    {
27702        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27703
27704        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27705            return CAFE24.SHOP_CURRENCY_INFO[undefined].fExchangeRate;
27706        } else {
27707            return CAFE24.SHOP_CURRENCY_INFO[iShopNo].fExchangeRate;
27708        }
27709    },
27710
27711    /**
27712     * 결제화폐 대비 참조화폐 환율 리턴
27713     * @param int iShopNo 쇼핑몰번호
27714     * @return float 결제화폐 대비 참조화폐 환율 (참조화폐가 없는 경우 null을 리턴합니다.)
27715     */
27716    getSubRate: function(iShopNo)
27717    {
27718        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27719
27720        if (typeof CAFE24.SHOP_CURRENCY_INFO[undefined] == 'object') {
27721            return CAFE24.SHOP_CURRENCY_INFO[undefined].fExchangeSubRate;
27722        } else {
27723            return CAFE24.SHOP_CURRENCY_INFO[iShopNo].fExchangeSubRate;
27724        }
27725    },
27726
27727    /**
27728     * 금액을 원하는 화폐코드의 제약조건(소수점 절삭)에 맞춰 리턴합니다.
27729     * @param float fPrice 금액
27730     * @param string aCurrencyInfo 원하는 화폐의 화폐 정보
27731     * @param bool bIsNumberFormat number_format 적용 유무
27732     * @return float|string
27733     */
27734    toPrice: function(fPrice, aCurrencyInfo, bIsNumberFormat)
27735    {
27736        // 소수점 아래 절삭
27737        var iPow = Math.pow(10, aCurrencyInfo['decimal_place']);
27738        fPrice = fPrice * iPow;
27739        if (aCurrencyInfo['round_method_type'] === 'F') {
27740            fPrice = Math.floor(fPrice);
27741        } else if (aCurrencyInfo['round_method_type'] === 'C') {
27742            fPrice = Math.ceil(fPrice);
27743        } else {
27744            fPrice = Math.round(fPrice);
27745        }
27746        fPrice = fPrice / iPow;
27747
27748        if (! fPrice) {
27749            // 가격이 없는 경우
27750            return 0;
27751
27752        } else if (bIsNumberFormat === true) {
27753            // 3자리씩 ,로 끊어서 리턴
27754            var sPrice = fPrice.toFixed(aCurrencyInfo['decimal_place']);
27755            var regexp = /^(-?[0-9]+)([0-9]{3})($|\.|,)/;
27756            while (regexp.test(sPrice)) {
27757                sPrice = sPrice.replace(regexp, "$1,$2$3");
27758            }
27759            return sPrice;
27760
27761        } else {
27762            // 숫자만 리턴
27763            return fPrice;
27764
27765        }
27766    }    
27767};
27768
27769var SHOP_PRICE = CAFE24.getDeprecatedNamespace('SHOP_PRICE');
27770/**
27771 * 화폐 포맷
27772 */
27773CAFE24.SHOP_CURRENCY_FORMAT = {
27774    /**
27775     * 어드민 페이지인지
27776     * @var bool
27777     */
27778    _bIsAdmin: /^\/(admin\/php|disp\/admin|exec\/admin)\//.test(location.pathname) ? true : false,
27779
27780    /**
27781     * iShopNo 쇼핑몰의 결제화폐 포맷을 리턴합니다.
27782     * @param int iShopNo 쇼핑몰번호
27783     * @return array head,tail
27784     */
27785    getShopCurrencyFormat: function(iShopNo)
27786    {
27787        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27788
27789        // 결제화폐 코드
27790        var sCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopCurrencyInfo.currency_code;
27791
27792        if (CAFE24.SHOP_CURRENCY_FORMAT._bIsAdmin === true) {
27793            // 어드민
27794
27795            // 기준화폐 코드
27796            var sBaseCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aBaseCurrencyInfo.currency_code;
27797
27798            if (sCurrencyCode === sBaseCurrencyCode) {
27799                // 결제화폐와 기준화폐가 동일한 경우
27800                return {
27801                    'head': '',
27802                    'tail': ''
27803                };
27804
27805            } else {
27806                return {
27807                    'head': sCurrencyCode + ' ',
27808                    'tail': ''
27809                };
27810            }
27811
27812        } else {
27813            // 프론트
27814            if (CAFE24.SHOP_CURRENCY_INFO[iShopNo].bPresentmentEnabled === true) {
27815                return CAFE24.SHOP_CURRENCY_INFO[iShopNo].aFrontPresentmentCurrencyFormat;
27816            }
27817            return CAFE24.SHOP_CURRENCY_INFO[iShopNo].aFrontCurrencyFormat;
27818        }
27819    },
27820
27821    /**
27822     * iShopNo 쇼핑몰의 참조화폐의 포맷을 리턴합니다.
27823     * @param int iShopNo 쇼핑몰번호
27824     * @return array head,tail
27825     */
27826    getShopSubCurrencyFormat: function(iShopNo)
27827    {
27828        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27829
27830        // 참조화폐 정보
27831        var aSubCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopSubCurrencyInfo;
27832
27833        if (! aSubCurrencyInfo) {
27834            // 참조화폐가 없으면
27835            return {
27836                'head': '',
27837                'tail': ''
27838            };
27839
27840        } else if (CAFE24.SHOP_CURRENCY_FORMAT._bIsAdmin === true) {
27841            // 어드민
27842            return {
27843                'head': '(' + aSubCurrencyInfo.currency_code + ' ',
27844                'tail': ')'
27845            };
27846
27847        } else {
27848            // 프론트
27849            return CAFE24.SHOP_CURRENCY_INFO[iShopNo].aFrontSubCurrencyFormat;
27850        }
27851
27852    },
27853
27854    /**
27855     * 쇼핑몰의 기준화폐의 포맷을 리턴합니다.
27856     * @param int iShopNo 쇼핑몰번호
27857     * @return array head,tail
27858     */
27859    getBaseCurrencyFormat: function(iShopNo)
27860    {
27861        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27862
27863        // 기준화폐 코드
27864        var sBaseCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aBaseCurrencyInfo.currency_code;
27865
27866        // 결제화폐 코드
27867        var sCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopCurrencyInfo.currency_code;
27868
27869        if (sCurrencyCode === sBaseCurrencyCode) {
27870            // 기준화폐와 결제화폐가 동일하면
27871            return {
27872                'head': '',
27873                'tail': ''
27874            };
27875
27876        } else {
27877            // 어드민
27878            return {
27879                'head': '(' + sBaseCurrencyCode + ' ',
27880                'tail': ')'
27881            };
27882
27883        }
27884    },
27885
27886    /**
27887     * 금액 입력란 화폐 포맷용 head,tail
27888     * @param int iShopNo 쇼핑몰번호
27889     * @return array head,tail
27890     */
27891    getInputFormat: function(iShopNo)
27892    {
27893        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27894
27895        var sCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopCurrencyInfo;
27896
27897        // 멀티쇼핑몰이 아니고 단위가 '원화'인 경우
27898        if (SHOP.isMultiShop() === false && sCurrencyCode === 'KRW') {
27899            return {
27900                'head': '',
27901                'tail': '원'
27902            };
27903
27904        } else {
27905            return {
27906                'head': '',
27907                'tail': sCurrencyCode
27908            };
27909        }
27910    },
27911
27912    /**
27913     * 해당몰 결제 화폐 코드 반환
27914     * ECHOSTING-266141 대응
27915     * 국문 기본몰 일 경우에는 화폐코드가 아닌 '원' 으로 반환
27916     *
27917     * @param int iShopNo 쇼핑몰번호
27918     * @return string currency_code
27919     */
27920    getCurrencyCode: function(iShopNo)
27921    {
27922        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27923
27924        var sCurrencyCode = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopCurrencyInfo.currency_code;
27925
27926        // 멀티쇼핑몰이 아니고 단위가 '원화'인 경우
27927        if (SHOP.isMultiShop() === false && sCurrencyCode === 'KRW') {
27928            return '원';
27929        } else {
27930            return sCurrencyCode;
27931        }
27932    }
27933
27934};
27935
27936var SHOP_CURRENCY_FORMAT = CAFE24.getDeprecatedNamespace('SHOP_CURRENCY_FORMAT');
27937
27938/**
27939 * 금액 포맷
27940 */
27941CAFE24.SHOP_PRICE_FORMAT = {
27942    /**
27943     * iShopNo 쇼핑몰의 결제화폐에 맞도록 하고 포맷팅하여 리턴합니다.
27944     * @param float fPrice 금액
27945     * @param int iShopNo 쇼핑몰번호
27946     * @return string
27947     */
27948    toShopPrice: function(fPrice, iShopNo)
27949    {
27950        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27951
27952        var aFormat = CAFE24.SHOP_CURRENCY_FORMAT.getShopCurrencyFormat(iShopNo);
27953        var sPrice = CAFE24.SHOP_PRICE.toShopPrice(fPrice, true, iShopNo);
27954        return aFormat.head + sPrice + aFormat.tail;
27955    },
27956
27957    /**
27958     * iShopNo 쇼핑몰의 Presentment 화폐에 맞도록 하고 포맷팅하여 리턴합니다. (화면 표시용)
27959     * Global-e Presentment 활성 시 환율 적용 + Presentment 화폐 포맷 사용,
27960     * 비활성 시 기본 결제화폐 포맷 사용 (toShopPrice와 동일 동작)
27961     * @param float fPrice 금액
27962     * @param int iShopNo 쇼핑몰번호
27963     * @return string
27964     */
27965    toShopPricePresentment: function(fPrice, iShopNo)
27966    {
27967        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27968
27969        var oCurrencyInfo = CAFE24.SHOP_CURRENCY_INFO[iShopNo] || CAFE24.SHOP_CURRENCY_INFO[undefined];
27970        var aFormat;
27971        if (oCurrencyInfo && oCurrencyInfo.bPresentmentEnabled === true) {
27972            aFormat = oCurrencyInfo.aFrontPresentmentCurrencyFormat;
27973        } else {
27974            aFormat = CAFE24.SHOP_CURRENCY_FORMAT.getShopCurrencyFormat(iShopNo);
27975        }
27976        var sPrice = CAFE24.SHOP_PRICE.toShopPricePresentment(fPrice, true, iShopNo);
27977        return aFormat.head + sPrice + aFormat.tail;
27978    },
27979
27980    /**
27981     * iShopNo 쇼핑몰의 참조화폐에 맞도록 하고 포맷팅하여 리턴합니다.
27982     * @param float fPrice 금액
27983     * @param int iShopNo 쇼핑몰번호
27984     * @return string
27985     */
27986    toShopSubPrice: function(fPrice, iShopNo)
27987    {
27988        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
27989
27990        var aFormat = CAFE24.SHOP_CURRENCY_FORMAT.getShopSubCurrencyFormat(iShopNo);
27991        var sPrice = CAFE24.SHOP_PRICE.toShopSubPrice(fPrice, true, iShopNo);
27992        return aFormat.head + sPrice + aFormat.tail;
27993    },
27994
27995    /**
27996     * 쇼핑몰의 기준화폐에 맞도록 하고 포맷팅하여 리턴합니다.
27997     * @param float fPrice 금액
27998     * @param int iShopNo 쇼핑몰번호
27999     * @return string
28000     */
28001    toBasePrice: function(fPrice, iShopNo)
28002    {
28003        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28004
28005        var aFormat = CAFE24.SHOP_CURRENCY_FORMAT.getBaseCurrencyFormat(iShopNo);
28006        var sPrice = CAFE24.SHOP_PRICE.toBasePrice(fPrice, true, iShopNo);
28007        return aFormat.head + sPrice + aFormat.tail;
28008    },
28009
28010    /**
28011     * 결제화폐 금액을 참조화폐 금액으로 변환하고 포맷팅하여 리턴합니다.
28012     * @param float fPrice 금액
28013     * @param int iShopNo 쇼핑몰번호
28014     * @return string
28015     */
28016    shopPriceToSubPrice: function(fPrice, iShopNo)
28017    {
28018        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28019
28020
28021        var aFormat = CAFE24.SHOP_CURRENCY_FORMAT.getShopSubCurrencyFormat(iShopNo);
28022        var sPrice = CAFE24.SHOP_PRICE.shopPriceToSubPrice(fPrice, true, iShopNo);
28023
28024        if (CAFE24.CURRENCY_INFO.isUseReferenceCurrency() !== true) { return sPrice; }
28025
28026        return '<span class="eRefPriceUnitHead">' + aFormat.head + '</span><span class="eRefPrice">' + sPrice + '</span><span class="eRefPriceUnitTail">'  + aFormat.tail + '</span>';
28027    },
28028
28029
28030    /**
28031     * 금액을 적립금 단위 명칭 설정에 따라 반환
28032     * @param float fPrice 금액
28033     * @return float|string
28034     */
28035    toShopMileagePrice: function (fPrice, iShopNo) {
28036        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28037        
28038        var sPrice = CAFE24.SHOP_PRICE.toShopPrice(fPrice, true, iShopNo);
28039        if (typeof sMileageUnit !== 'undefined' && CAFE24.UTIL.trim(sMileageUnit) != '') {
28040            sConvertMileageUnit = sMileageUnit.replace('[:PRICE:]', sPrice);
28041            return sConvertMileageUnit;
28042        } else {
28043            return CAFE24.SHOP_PRICE_FORMAT.toShopPrice(fPrice);
28044        }
28045    },
28046
28047    /**
28048     * 금액을 예치금 단위 명칭 설정에 따라 반환
28049     * @param float fPrice 금액
28050     * @return float|string
28051     */
28052    toShopDepositPrice: function (fPrice, iShopNo) {
28053        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28054
28055        var sPrice = CAFE24.SHOP_PRICE.toShopPrice(fPrice, true, iShopNo);
28056        if (typeof sDepositUnit !== 'undefined' || CAFE24.UTIL.trim(sDepositUnit) != '') {
28057            return sPrice + sDepositUnit;
28058        } else {
28059            return CAFE24.SHOP_PRICE_FORMAT.toShopPrice(fPrice);
28060        }
28061    },
28062
28063    /**
28064     * ECHOSTING-610183 Presentment: 금액을 적립금 단위 명칭 설정에 따라 presentment 화폐로 반환
28065     * @param float fPrice 금액 (KRW 기준)
28066     * @param int iShopNo 쇼핑몰번호
28067     * @return string
28068     */
28069    toShopMileagePricePresentment: function (fPrice, iShopNo) {
28070        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28071
28072        var sPrice = CAFE24.SHOP_PRICE.toShopPricePresentment(fPrice, true, iShopNo);
28073        if (typeof sMileageUnit !== 'undefined' && CAFE24.UTIL.trim(sMileageUnit) != '') {
28074            return sMileageUnit.replace('[:PRICE:]', sPrice);
28075        } else {
28076            return CAFE24.SHOP_PRICE_FORMAT.toShopPricePresentment(fPrice);
28077        }
28078    },
28079
28080    /**
28081     * ECHOSTING-610183 Presentment: 금액을 예치금 단위 명칭 설정에 따라 presentment 화폐로 반환
28082     * @param float fPrice 금액 (KRW 기준)
28083     * @param int iShopNo 쇼핑몰번호
28084     * @return string
28085     */
28086    toShopDepositPricePresentment: function (fPrice, iShopNo) {
28087        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28088
28089        var sPrice = CAFE24.SHOP_PRICE.toShopPricePresentment(fPrice, true, iShopNo);
28090        if (typeof sDepositUnit !== 'undefined' || CAFE24.UTIL.trim(sDepositUnit) != '') {
28091            return sPrice + sDepositUnit;
28092        } else {
28093            return CAFE24.SHOP_PRICE_FORMAT.toShopPricePresentment(fPrice);
28094        }
28095    },
28096
28097    /**
28098     * 금액을 부가결제수단(통합포인트) 단위 명칭 설정에 따라 반환
28099     * @param float fPrice 금액
28100     * @return float|string
28101     */
28102    toShopAddpaymentPrice: function (fPrice, sAddpaymentUnit, iShopNo) {
28103        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28104
28105        var sPrice = CAFE24.SHOP_PRICE.toShopPrice(fPrice, true, iShopNo);
28106        if (typeof sDepositUnit !== 'undefined' || CAFE24.UTIL.trim(sDepositUnit) != '') {
28107            return sPrice + sAddpaymentUnit;
28108        } else {
28109            return CAFE24.SHOP_PRICE_FORMAT.toShopPrice(fPrice);
28110        }
28111    },
28112
28113    /**
28114     * 포맷을 제외한 금액정보만 리턴합니다.
28115     * @param {string} sFormattedPrice
28116     * @returns {string}
28117     */
28118    detachFormat: function(sFormattedPrice) {
28119        if (typeof sFormattedPrice === 'undefined' || sFormattedPrice === null) {
28120            return '0';
28121        }
28122
28123        var sPattern = /[0-9.]/;
28124        var sPrice = '';
28125        for (var i = 0; i < sFormattedPrice.length; i++) {
28126            if (sPattern.test(sFormattedPrice[i])) {
28127                sPrice += sFormattedPrice[i];
28128            }
28129        }
28130
28131        return sPrice;
28132    }
28133};
28134
28135var SHOP_PRICE_FORMAT = CAFE24.getDeprecatedNamespace('SHOP_PRICE_FORMAT');
28136CAFE24.SHOP_PRICE_UTIL = {
28137    /**
28138     * iShopNo 쇼핑몰의 결제화폐 금액 입력폼으로 만듭니다.
28139     * @param Element elem 입력폼
28140     * @param bool bUseMinus 마이너스 입력 사용 여부
28141     */
28142    toShopPriceInput: function(elem, iShopNo, bUseMinus)
28143    {
28144        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28145
28146        var iDecimalPlace = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopCurrencyInfo.decimal_place;
28147        bUseMinus ? CAFE24.SHOP_PRICE_UTIL._toPriceInput(elem, i
28147DecimalPlace, bUseMinus) : CAFE24.SHOP_PRICE_UTIL._toPriceInput(elem, iDecimalPlace);
28148    },
28149
28150    /**
28151     * iShopNo 쇼핑몰의 참조화폐 금액 입력폼으로 만듭니다.
28152     * @param Element elem 입력폼
28153     */
28154    toShopSubPriceInput: function(elem, iShopNo)
28155    {
28156        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28157
28158        var iDecimalPlace = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aShopSubCurrencyInfo.decimal_place;
28159        CAFE24.SHOP_PRICE_UTIL._toPriceInput(elem, iDecimalPlace);
28160    },
28161
28162    /**
28163     * iShopNo 쇼핑몰의 기준화폐 금액 입력폼으로 만듭니다.
28164     * @param Element elem 입력폼
28165     */
28166    toBasePriceInput: function(elem, iShopNo)
28167    {
28168        iShopNo = parseInt(iShopNo) || CAFE24.SDE_SHOP_NUM;
28169
28170        var iDecimalPlace = CAFE24.SHOP_CURRENCY_INFO[iShopNo].aBaseCurrencyInfo.decimal_place;
28171        CAFE24.SHOP_PRICE_UTIL._toPriceInput(elem, iDecimalPlace);
28172    },
28173
28174    /**
28175     * 소수점 iDecimalPlace까지만 입력 가능하도록 처리
28176     * @param Element elem 입력폼
28177     * @param int iDecimalPlace 허용 소수점
28178     * @param bool bUseMinus 마이너스 입력 사용 여부
28179     */
28180    _toPriceInput: function(elem, iDecimalPlace, bUseMinus)
28181    {
28182        attachEvent(elem, 'keyup', function(e) {
28183            e = e || window.event;
28184            bUseMinus ? replaceToMinusPrice(e.srcElement) : replaceToPrice(e.srcElement);
28185        });
28186        attachEvent(elem, 'blur', function(e) {
28187            e = e || window.event;
28188            bUseMinus ? replaceToMinusPrice(e.srcElement) : replaceToPrice(e.srcElement);
28189        });
28190
28191        // 추가금액에서 마이너스를 입력받기 위해 사용
28192        function replaceToMinusPrice(target) {
28193            var value = target.value;
28194
28195            var regExpTest = new RegExp('^[0-9]*' + (iDecimalPlace ? '' : '\\.[0-9]{0, ' + iDecimalPlace + '}') + '$');
28196
28197            if (regExpTest.test(value) === false) {
28198                value = value.replace(/[^0-9.|\-]/g, '');
28199                if (parseInt(iDecimalPlace)) {
28200                    value = value.replace(/^([0-9]+\.[0-9]+)\.+.*$/, '$1');
28201                    value = value.replace(new RegExp('(\\.[0-9]{' + iDecimalPlace + '})[0-9]*$'), '$1');
28202                } else {
28203                    value = value.replace(/[^(0-9|\-)]/g, '');
28204                }
28205                target.value = value;
28206            }
28207        }
28208
28209        function replaceToPrice(target)
28210        {
28211            var value = target.value;
28212
28213            var regExpTest = new RegExp('^[0-9]*' + (iDecimalPlace ? '' : '\\.[0-9]{0, ' + iDecimalPlace + '}') + '$');
28214            if (regExpTest.test(value) === false) {
28215                value = value.replace(/[^0-9.]/g, '');
28216                if (parseInt(iDecimalPlace)) {
28217                    value = value.replace(/^([0-9]+\.[0-9]+)\.+.*$/, '$1');
28218                    value = value.replace(new RegExp('(\\.[0-9]{' + iDecimalPlace + '})[0-9]*$'), '$1');
28219                } else {
28220                    value = value.replace(/\.+[0-9]*$/, '');
28221                }
28222                target.value = value;
28223            }
28224        }
28225
28226        function attachEvent(elem, sEventName, fn)
28227        {
28228            if (elem.addEventListener) {
28229                elem.addEventListener(sEventName, fn, false);
28230
28231            } else if (elem.attachEvent) {
28232                elem.attachEvent("on" + sEventName, fn);
28233            }
28234        }
28235
28236    }
28237};
28238
28239if (window.jQuery !== undefined) {
28240    $.fn.extend({
28241        toShopPriceInput: function(iShopNo)
28242        {
28243            return this.each(function() {
28244                var iElementShopNo = $(this).data('shop_no') || iShopNo;
28245                CAFE24.SHOP_PRICE_UTIL.toShopPriceInput(this, iElementShopNo);
28246            });
28247        },
28248        toShopSubPriceInput: function(iShopNo)
28249        {
28250            return this.each(function() {
28251                var iElementShopNo = $(this).data('shop_no') || iShopNo;
28252                CAFE24.SHOP_PRICE_UTIL.toShopSubPriceInput(this, iElementShopNo);
28253            });
28254        },
28255        toBasePriceInput: function(iShopNo)
28256        {
28257            return this.each(function() {
28258                var iElementShopNo = $(this).data('shop_no') || iShopNo;
28259                CAFE24.SHOP_PRICE_UTIL.toBasePriceInput(this, iElementShopNo);
28260            });
28261        }
28262    });
28263}
28264
28265// EC$ 별칭용
28266if (typeof window.EC$ === 'function') {
28267    EC$.fn.extend({
28268        toShopPriceInput: function(iShopNo)
28269        {
28270            return this.each(function() {
28271                var iElementShopNo = EC$(this).data('shop_no') || iShopNo;
28272                CAFE24.SHOP_PRICE_UTIL.toShopPriceInput(this, iElementShopNo);
28273            });
28274        },
28275        toShopSubPriceInput: function(iShopNo)
28276        {
28277            return this.each(function() {
28278                var iElementShopNo = EC$(this).data('shop_no') || iShopNo;
28279                CAFE24.SHOP_PRICE_UTIL.toShopSubPriceInput(this, iElementShopNo);
28280            });
28281        },
28282        toBasePriceInput: function(iShopNo)
28283        {
28284            return this.each(function() {
28285                var iElementShopNo = EC$(this).data('shop_no') || iShopNo;
28286                CAFE24.SHOP_PRICE_UTIL.toBasePriceInput(this, iElementShopNo);
28287            });
28288        }
28289    });
28290}
28291
28292var SHOP_PRICE_UTIL = CAFE24.getDeprecatedNamespace('SHOP_PRICE_UTIL');
28293CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE = {
28294    oBundleConfig: {},
28295
28296    iProductQuantity: 0,
28297
28298    init: function(oInit)
28299    {
28300        if (typeof(oInit) === 'object') {
28301            this.oBundleConfig = oInit;
28302        } else {
28303            if (sBundlePromotion === '' || typeof(sBundlePromotion) === 'undefined') {
28304                return;
28305            }
28306            this.oBundleConfig = CAFE24.UTIL.parseJSON(sBundlePromotion);
28307        }
28308        // 강제로 후킹
28309        buy_unit = 1;
28310        product_min = 1;
28311        product_max = 0;
28312
28313        EC$.data(document,'BundlePromotion', true);
28314    },
28315    getQuantityStep: function(iProductNum)
28316    {
28317        return this.oBundleConfig[iProductNum].bundle_quantity + 1;
28318    },
28319    /**
28320     * 실제 UI의 수량대신 1+N 이벤트로 인해 후킹된 상품 수량을 리턴
28321     */
28322    getQuantity: function(iQuantity, iProductNum)
28323    {
28324        var iReturn = iQuantity;
28325        if (typeof(this.oBundleConfig[iProductNum]) === 'undefined') {
28326            return iReturn;
28327        }
28328
28329        iReturn = Math.ceil(iQuantity / this.getQuantityStep(iProductNum));
28330
28331        return iReturn;
28332    },
28333    /**
28334     * 정확한 구매 수량이 맞는지 검증
28335     */
28336    isValidQuantity: function(aQuantity, iProductNum)
28337    {
28338        var bReturn = true;
28339        if (typeof(this.oBundleConfig[iProductNum]) === 'undefined') {
28340            return bReturn;
28341        }
28342
28343        if (this.isValidQuantityCheck(aQuantity, iProductNum) === false) {
28344            alert(this.getAlertMessage([iProductNum]));
28345            return false;
28346        }
28347        return bReturn;
28348    },
28349    isValidQuantityCheck: function(aQuantity, iProductNum)
28350    {
28351        var iQuantityStep = this.getQuantityStep(iProductNum);
28352
28353        if (this.oBundleConfig[iProductNum].bundle_apply_type === 'P') {
28354            CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.iProductQuantity = 0;
28355            EC$.map(aQuantity, function(pv) {
28356                CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.iProductQuantity += pv;
28357            });
28358            return (CAFE24.SHOP_FRONT_PRODUCT_DETAIL_BUNDLE.iProductQuantity % iQuantityStep) === 0;
28359        }
28360
28361        if (this.oBundleConfig[iProductNum].bundle_apply_type === 'I') {
28362            for (var i in aQuantity) {
28363                if (aQuantity.hasOwnProperty(i) === false) {
28364                    continue;
28365                }
28366                if (aQuantity[i] % iQuantityStep !== 0) {
28367                    return false;
28368                }
28369            }
28370        }
28371        return true;
28372    },
28373    getAlertMessage: function(iProductNum)
28374    {
28375        var sSubject = this.oBundleConfig[iProductNum].bundle_apply_type === 'P' ? '옵션에 상관없이' : '동일한 옵션으로';
28376        var sReturn = '1+%s 이벤트상품입니다.\n'+sSubject+' 수량을 %s개 단위로 구매해주세요.';
28377        return sprintf(__(sReturn), this.oBundleConfig[iProductNum].bundle_quantity, this.getQuantityStep(iProductNum));
28378    }
28379};
28380
28381var EC_SHOP_FRONT_PRODUCT_DEATAIL_BUNDLE = CAFE24.getDeprecatedNamespace('SHOP_FRONT_PRODUCT_DETAIL_BUNDLE');
28382
28383(function(window) {
28384    window.htmlentities = {
28385        /**
28386         * Converts a string to its html characters completely.
28387         *
28388         * @param {String} str String with unescaped HTML characters
28389         **/
28390        encode: function(str) {
28391            var buf = [];
28392
28393            for (var i=str.length-1; i>=0; i--) {
28394                buf.unshift(['&#', str[i].charCodeAt(), ';'].join(''));
28395            }
28396
28397            return buf.join('');
28398        },
28399        /**
28400         * Converts an html characterSet into its original character.
28401         *
28402         * @param {String} str htmlSet entities
28403         **/
28404        decode: function(str) {
28405            return str.replace(/&#(\d+);/g, function(match, dec) {
28406                return String.fromCharCode(dec);
28407            });
28408        }
28409    };
28410})(window);
28411
28412CAFE24.CRYPTOKEY = (function () {
28413    const algorithm = {
28414        name: "AES-CBC",
28415        length: 256
28416    };
28417
28418    const DB_NAME = "d1";
28419    const DB_VERSION = 1;
28420    const STORE_NAME = "s1";
28421    const STORE_KEY = "eto3";
28422    const KEY_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 1 day
28423
28424    function openDB() {
28425        return new Promise((resolve, reject) => {
28426            const request = window.indexedDB.open(DB_NAME, DB_VERSION);
28427
28428            request.onupgradeneeded = function (event) {
28429                const db = event.target.result;
28430                if (!db.objectStoreNames.contains(STORE_NAME)) {
28431                    db.createObjectStore(STORE_NAME);
28432                }
28433            };
28434
28435            request.onsuccess = function () {
28436                resolve(request.result);
28437            };
28438
28439            request.onerror = function () {
28440                reject(request.error);
28441            };
28442        });
28443    }
28444
28445    async function saveKey(key) {
28446        const db = await openDB();
28447
28448        return new Promise((resolve, reject) => {
28449            const tx = db.transaction(STORE_NAME, "readwrite");
28450            const store = tx.objectStore(STORE_NAME);
28451            const request = store.put({ ts: Date.now(), key: key }, STORE_KEY);
28452
28453            request.onsuccess = function () {
28454                resolve(true);
28455            };
28456
28457            request.onerror = function () {
28458                reject(request.error);
28459            };
28460
28461            tx.oncomplete = function () {
28462                db.close();
28463            };
28464        });
28465    }
28466
28467    async function loadKey() {
28468        const db = await openDB();
28469
28470        return new Promise((resolve, reject) => {
28471            const tx = db.transaction(STORE_NAME, "readonly");
28472            const store = tx.objectStore(STORE_NAME);
28473            const request = store.get(STORE_KEY);
28474
28475            request.onsuccess = async function () {
28476                const record = request.result;
28477
28478                if (!record || !record.key) {
28479                    resolve(null);
28480                    return;
28481                }
28482
28483                if (!record.ts || (Date.now() - record.ts) > KEY_MAX_AGE_MS) {
28484                    tx.oncomplete = function () {
28485                        db.close();
28486                    };
28487
28488                    try {
28489                        await CAFE24.CRYPTOKEY.deleteKey();
28490                    } catch (e) {
28491                        // ignore delete failure
28492                    }
28493
28494                    resolve(null);
28495                    return;
28496                }
28497
28498                resolve(record.key);
28499            };
28500
28501            request.onerror = function () {
28502                reject(request.error);
28503            };
28504
28505            tx.oncomplete = function () {
28506                db.close();
28507            };
28508        });
28509    }
28510
28511    async function deleteKey() {
28512        const db = await openDB();
28513
28514        return new Promise((resolve, reject) => {
28515            const tx = db.transaction(STORE_NAME, "readwrite");
28516            const store = tx.objectStore(STORE_NAME);
28517            const request = store.delete(STORE_KEY);
28518
28519            request.onsuccess = function () {
28520                resolve(true);
28521            };
28522
28523            request.onerror = function () {
28524                reject(request.error);
28525            };
28526
28527            tx.oncomplete = function () {
28528                db.close();
28529            };
28530        });
28531    }
28532
28533    async function generateKey() {
28534        const key = await window.crypto.subtle.generateKey(
28535            algorithm,
28536            false,
28537            ["encrypt", "decrypt"]
28538        );
28539        return key;
28540    }
28541
28542    async function encryptData(data, key) {
28543        const encodedData = new TextEncoder().encode(data);
28544        const iv = window.crypto.getRandomValues(new Uint8Array(16));
28545        const encryptedData = await window.crypto.subtle.encrypt(
28546            { name: "AES-CBC", iv: iv },
28547            key,
28548            encodedData
28549        );
28550        return { encryptedData, iv };
28551    }
28552
28553    async function decryptData(encryptedData, iv, key) {
28554        const decryptedData = await window.crypto.subtle.decrypt(
28555            { name: "AES-CBC", iv: iv },
28556            key,
28557            encryptedData
28558        );
28559        return new TextDecoder().decode(decryptedData);
28560    }
28561
28562    return {
28563        generateKey,
28564        saveKey,
28565        loadKey,
28566        deleteKey,
28567        encryptData,
28568        decryptData
28569    };
28570})();
28571
28572
28573CAFE24.CRYPTOKEY.encryptAndSave = async function (name, data) {
28574    let key = await CAFE24.CRYPTOKEY.loadKey();
28575
28576    if (!key) {
28577        key = await CAFE24.CRYPTOKEY.generateKey();
28578        await CAFE24.CRYPTOKEY.saveKey(key);
28579    }
28580
28581    const result = await CAFE24.CRYPTOKEY.encryptData(data, key);
28582    const encryptedData = new Uint8Array(result.encryptedData);
28583    const iv = new Uint8Array(result.iv);
28584
28585    window.sessionStorage.setItem(`eto4${name}`, JSON.stringify(Array.from(encryptedData)));
28586    window.sessionStorage.setItem(`eto5${name}`, JSON.stringify(Array.from(iv)));
28587};
28588
28589
28590CAFE24.CRYPTOKEY.decryptAndLoad = async function (name) {
28591    const loadedKey = await CAFE24.CRYPTOKEY.loadKey();
28592    if (!loadedKey) return null;
28593
28594    const encryptedData = window.sessionStorage.getItem(`eto4${name}`);
28595    const iv = window.sessionStorage.getItem(`eto5${name}`);
28596
28597    if (!encryptedData || !iv) return null;
28598
28599    const encryptedArray = new Uint8Array(JSON.parse(encryptedData));
28600    const ivArray = new Uint8Array(JSON.parse(iv));
28601
28602    return await CAFE24.CRYPTOKEY.decryptData(
28603        encryptedArray.buffer,
28604        ivArray.buffer,
28605        loadedKey
28606    );
28607};
28608/**
28609 * 프론트 로그인 상태 가드
28610 *
28611 * bfcache 복원(pageshow persisted) 또는 브라우저 HTTP 캐시 재사용으로 표시된 문서가
28612 * 현재 로그인 상태(ec_mem_level 쿠키)와 다르면 재로드하여 화면과 실제 상태를 일치시킨다.
28613 * - bfcache 경로: JS 힙이 보존되므로 렌더 시점 쿠키 스냅샷과 현재 쿠키를 비교
28614 * - HTTP 캐시 재사용 경로: JS가 재실행되어 스냅샷이 무효이므로 서버 렌더 산출물(로그인/로그아웃 모듈 DOM)과 현재 쿠키를 비교
28615 * 불일치일 때만 재로드하므로 비로그인 사용자의 bfcache 복원(뒤로가기 목록/스크롤 유지)은 그대로 동작한다.
28616 *
28617 * @package app/Manage
28618 * @since 2026. 7. 30.
28619 * @see https://jira.simplexi.com/browse/ECQAUNIT-75013
28620 * @see https://jira.simplexi.com/browse/ECHOSTING-385628
28621 * @see https://jira.simplexi.com/browse/ECHOSTING-635932
28622 * @see https://jira.simplexi.com/browse/ECHOSTING-636994
28623 * @see https://jira.simplexi.com/browse/ECHOSTING-636472
28624 * @see https://jira.simplexi.com/browse/ECHOSTING-637169
28625 * @see https://jira.simplexi.com/browse/PLUSAPP-56623
28626 */
28627(function() {
28628    // 서브프레임에서는 동작하지 않는다 - 가드의 목적은 캐시 복원된 "최상위 문서" 의 로그인 표시 불일치
28629    // 교정이고, 프레임 안의 location.reload() 는 프레임만 다시 그려 교정 효과가 없으면서 브레이크
28630    // (sessionStorage) 만 공유해 부모를 방해한다. 로그인 페이지가 same-origin 으로 품는
28631    // /protected/loginSns.html iframe 이 이 경로다 (appMemberFrontDispLogin -> agreement_layer.tpl).
28632    // pageshow 리스너 등록 전에 반환해야 프레임 문서가 재로드 판정 자체에 진입하지 않는다.
28633    // window.top !== window.self 는 참조 비교뿐이라 cross-origin 상위에서도 SecurityError 가 없다.
28634    if (window.top !== window.self) {
28635        return;
28636    }
28637
28638    // 브레이크는 "이 문서를 이미 한 번 리로드했다" 는 기억이므로 문서 단위여야 한다. 단일 키였을 때
28639    // (1) same-origin iframe 이 부모와 같은 칸을 번갈아 덮어써 브레이크가 매번 풀렸고 (무한 재로드의 직접 원인)
28640    // (2) 같은 탭에서 다른 문서로 이동하면 앞 문서의 기억이 지워져 되돌아올 때 또 reload 됐다.
28641    // (1) 은 위 서브프레임 반환으로도 막히지만 안전장치 실패였던 만큼 이중으로 건다. [ECHOSTING-636994]
28642    var sBrakeKey = 'EC_STATE_GUARD_RELOADED_' + location.pathname;
28643
28644    // 현재 로그인 여부 (로그인시 생성/로그아웃시 삭제되는 ec_mem_level 쿠키 기준)
28645    var hasAuthCookie = function() {
28646        return /(?:^|;\s*)ec_mem_level=/.test(document.cookie);
28647    };
28648
28649    // 렌더 시점 로그인 상태 스냅샷 (bfcache 복원시 힙에 보존됨)
28650    var bRenderedAuth = hasAuthCookie();
28651
28652    // 서버가 렌더한 로그인 상태
28653    // 1순위: 서버 출력값(CAFE24.FRONTEND.IS_RENDERED_LOGIN) - HTML 에 박혀 있어 캐시 재사용 문서에서도 렌더 시점 값이 유지되며 스킨 무관
28654    // 2순위: 로그인/로그아웃 모듈 DOM (서버 출력값이 없는 구버전 캐시 문서 폴백. 판별 불가시 null - 동작하지 않음)
28655    var getRenderedAuthState = function() {
28656        try {
28657            if (window.CAFE24 && CAFE24.FRONTEND && (CAFE24.FRONTEND.IS_RENDERED_LOGIN === 'T' || CAFE24.FRONTEND.IS_RENDERED_LOGIN === 'F')) {
28658                return CAFE24.FRONTEND.IS_RENDERED_LOGIN === 'T';
28659            }
28660        } catch (e) {
28661        }
28662        var oLogon = document.querySelector('.xans-layout-statelogon, .xans-layout-logon');
28663        var oLogoff = document.querySelector('.xans-layout-statelogoff');
28664        if (oLogon !== null && oLogoff === null) return true;
28665        if (oLogoff !== null && oLogon === null) return false;
28666        return null;
28667    };
28668
28669    // 일반 로드에서는 네트워크 전송이 없었고 reload 가 아닌 문서만 HTTP 캐시 재사용으로 판정한다.
28670    // 새 응답이나 reload 응답까지 교정하면 서버 상태와 쿠키가 계속 어긋나는 멀티샵 환경에서
28671    // sessionStorage 사용이 제한될 때 reload 를 멈출 수 없다. [ECHOSTING-636472, ECHOSTING-637169, PLUSAPP-56623]
28672    var isCacheReusedDocument = function() {
28673        try {
28674            if (!window.performance || typeof performance.getEntriesByType !== 'function') return false;
28675            var aEntries = performance.getEntriesByType('navigation');
28676            if (aEntries.length < 1 || typeof aEntries[0].transferSize !== 'number') return false;
28677            return aEntries[0].type !== 'reload' && aEntries[0].transferSize === 0;
28678        } catch (e) {
28679            return false;
28680        }
28681    };
28682
28683    // 동일 URL 재발동 방지 (서버 렌더와 쿠키가 계속 어긋나는 예외 상황에서 재로드 반복 차단)
28684    var reloadOnce = function() {
28685        // 디자인 편집기 미리보기(blob:)는 쿠키를 사용할 수 없어 서버 렌더 상태와 비교할 수 없다.
28686        if (location.protocol === 'blob:') {
28687            return;
28688        }
28689        try {
28690            if (window.sessionStorage) {
28691                if (sessionStorage.getItem(sBrakeKey) === location.href) return;
28692                sessionStorage.setItem(sBrakeKey, location.href);
28693            }
28694        } catch (e) {
28695        }
28696        location.reload();
28697    };
28698
28699    var clearBrake = function() {
28700        try {
28701            if (window.sessionStorage) sessionStorage.removeItem(sBrakeKey);
28702        } catch (e) {
28703        }
28704    };
28705
28706    window.addEventListener('pageshow', function(e) {
28707        // bfcache 복원: 렌더 시점 스냅샷과 현재 쿠키 비교
28708        if (e.persisted === true && hasAuthCookie() !== bRenderedAuth) {
28709            reloadOnce();
28710            return;
28711        }
28712
28713        // 일반 로드(HTTP 캐시 재사용 포함): 서버 렌더 상태와 현재 쿠키 비교
28714        var bRenderedState = getRenderedAuthState();
28715        if (bRenderedState !== null && bRenderedState !== hasAuthCookie() && isCacheReusedDocument()) {
28716            reloadOnce();
28717            return;
28718        }
28719
28720        clearBrake();
28721    });
28722})();
28723
28724/**
28725 * 비동기식 데이터
28726 */
28727var CAPP_ASYNC_METHODS = {
28728    STATUS: 'unready',
28729    DEBUG: false,
28730    IS_LOGIN: (document.cookie.match(/(?:^| |;)iscache=F/) ? true : false),
28731    EC_PATH_ROLE: EC$('meta[name="path_role"]').attr('content') || '',
28732    aDatasetList: [],
28733    $xansMyshopMain: EC$('.xans-myshop-main'),
28734    init: function()
28735    {
28736        CAPP_ASYNC_METHODS.STATUS = 'ready';
28737    	var bDebug = CAPP_ASYNC_METHODS.DEBUG;
28738
28739        var aUseModules = [];
28740        var aNoCachedModules = [];
28741
28742        EC$(CAPP_ASYNC_METHODS.aDatasetList).each(function() {
28743            var sKey = this;
28744
28745            var oTarget = CAPP_ASYNC_METHODS[sKey];
28746
28747            if (bDebug) {
28748                console.log(sKey);
28749            }
28750            var bIsUse = oTarget.isUse();
28751            if (bDebug) {
28752                console.log('   isUse() : ' + bIsUse);
28753            }
28754
28755            if (bIsUse === true) {
28756                aUseModules.push(sKey);
28757
28758                if (oTarget.restoreCache === undefined || oTarget.restoreCache() === false) {
28759                    if (bDebug) {
28760                        console.log('   restoreCache() : true');
28761                    }
28762                    aNoCachedModules.push(sKey);
28763                }
28764            }
28765        });
28766
28767        if (aNoCachedModules.length > 0) {
28768            var sEditor = '';
28769            try {
28770                if (bEditor === true) {
28771                    // 에디터에서 접근했을 경우 임의의 상품 지정
28772                    sEditor = '&PREVIEW_SDE=1';
28773                }
28774            } catch (e) { }
28775
28776            var sPathRole = '&path_role=' + CAPP_ASYNC_METHODS.EC_PATH_ROLE;
28777            var sEcMobile = '&EC_MOBILE=' + EC_MOBILE;
28778
28779            // 모듈별 추가 파라미터 수집 (getParam 훅)
28780            var sModuleParam = '';
28781            for (var i = 0; i < aNoCachedModules.length; i++) {
28782                var oMod = CAPP_ASYNC_METHODS[aNoCachedModules[i]];
28783                if (typeof oMod.getParam === 'function') {
28784                    var sParam = oMod.getParam();
28785                    if (sParam) {
28786                        sModuleParam += '&' + sParam;
28787                    }
28788                }
28789            }
28790
28791            var sUrl = '/exec/front/manage/async?module=' + aNoCachedModules.join(',') + sModuleParam + sEditor + sPathRole + sEcMobile;
28792            EC$.ajax(
28793            {
28794                url: sUrl,
28795                dataType: 'json',
28796                success: function(aData)
28797                {
28798                	CAPP_ASYNC_METHODS.setData(aData, aUseModules);
28799                }
28800            });
28801
28802        } else {
28803        	CAPP_ASYNC_METHODS.setData({}, aUseModules);
28804
28805        }
28806    },
28807    setData: function(aData, aUseModules)
28808    {
28809        aData = aData || {};
28810
28811        EC$(aUseModules).each(function() {
28812            var sKey = this;
28813
28814            var oTarget = CAPP_ASYNC_METHODS[sKey];
28815
28816            if (oTarget.setData !== undefined && aData.hasOwnProperty(sKey) === true) {
28817                oTarget.setData(aData[sKey]);
28818            }
28819
28820            if (oTarget.execute !== undefined) {
28821                oTarget.execute();
28822            }
28823        });
28824
28825        CAPP_ASYNC_METHODS.STATUS = 'complete';
28826    },
28827
28828    _getCookie: function(sCookieName)
28829    {
28830        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
28831        var aCookieValue = document.cookie.match(re);
28832        return aCookieValue ? aCookieValue[1] : null;
28833    }
28834};
28835
28836var eCache = null;
28837if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
28838    CAFE24.CRYPTOKEY.decryptAndLoad('member_' + CAFE24.SDE_SHOP_NUM).then(function(o) {
28839        eCache = o;
28840    }).catch(function(err) {
28841        // console.log(err);
28842    });
28843}
28844
28845
28846/**
28847 * 비동기식 데이터 - 회원 정보
28848 */
28849CAPP_ASYNC_METHODS.aDatasetList.push('member');
28850CAPP_ASYNC_METHODS.member = {
28851    __sEncryptedString: null,
28852    __isAdult: 'F',
28853
28854    // 회원 데이터
28855    __sMemberId: null,
28856    __sHMemberId: null,
28857    __sName: null,
28858    __sNickName: null,
28859    __sGroupName: null,
28860    __sEmail: null,
28861    __sNewsMail: null,
28862    __sPhone: null,
28863    __sCellphone: null,
28864    __sSms: null,
28865    __sBirthday: null,
28866    __sGroupNo: null,
28867    __sBoardWriteName: null,
28868    __sAdditionalInformation: null,
28869    __sAuthenticationMethod: null,
28870    __sCreatedDate: null,
28871
28872    isUse: function()
28873    {
28874        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
28875            if (EC$('.xans-layout-statelogon, .xans-layout-logon').length > 0) {
28876                return true;
28877            }
28878
28879            if (CAPP_ASYNC_METHODS.recent.isUse() === true
28880                && typeof(EC_FRONT_JS_CONFIG_SHOP) !== 'undefined'
28881                && EC_FRONT_JS_CONFIG_SHOP.adult19Warning === 'T') {
28882                return true;
28883            }
28884
28885            if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('customer', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
28886                return true;
28887            }
28888
28889        } else {
28890            // 비 로그인 상태에서 삭제처리
28891            this.removeCache();
28892        }
28893
28894        return false;
28895    },
28896
28897    restoreCache: function()
28898    {
28899        // sessionStorage 지원 여부 확인
28900        if (!window.sessionStorage) {
28901            return false;
28902        }
28903
28904        // 데이터 복구 유무
28905        var bRestored = false;
28906
28907        try {
28908            // 데이터 복구
28909            var oCache = null;
28910            if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
28911                if (eCache) {
28912                    oCache = JSON.parse(eCache);
28913                }
28914            } else if (window.location.protocol === 'http:' && (typeof CryptoJS) == 'object') {
28915                if (window.sessionStorage.getItem('member_' + CAFE24.SDE_SHOP_NUM)) {
28916                    oCache = window.sessionStorage.getItem('member_' + CAFE24.SDE_SHOP_NUM);
28917                    oCache = CryptoJS.AES.decrypt(oCache, SHOP.getMallID() + '_' + EC_SDE_SHOP_NUM).toString(CryptoJS.enc.Utf8);
28918                    oCache = JSON.parse(oCache);
28919                }
28920            }
28921
28922            // expire 체크
28923            if (oCache.exp < Date.now()) {
28924                throw 'cache has expired.';
28925            }
28926
28927            // 데이터 체크
28928            if (typeof oCache.data.member_id === 'undefined'
28929                || oCache.data.member_id === ''
28930                || typeof oCache.data.h_member_id === 'undefined'
28931                || oCache.data.h_member_id === ''
28932                || typeof oCache.data.name === 'undefined'
28933                || typeof oCache.data.nick_name === 'undefined'
28934                || typeof oCache.data.group_name === 'undefined'
28935                || typeof oCache.data.group_no === 'undefined'
28936                || typeof oCache.data.email === 'undefined'
28937                || typeof oCache.data.news_mail === 'undefined'
28938                || typeof oCache.data.phone === 'undefined'
28939                || typeof oCache.data.cellphone === 'undefined'
28940                || typeof oCache.data.sms === 'undefined'
28941                || typeof oCache.data.birthday === 'undefined'
28942                || typeof oCache.data.board_write_name === 'undefined'
28943                || typeof oCache.data.additional_information === 'undefined'
28944                || typeof oCache.data.authentication_method === 'undefined'
28945                || typeof oCache.data.created_date === 'undefined'
28946            ) {
28947                throw 'Invalid cache data.';
28948            }
28949
28950            // 데이터 복구
28951            this.__sMemberId = oCache.data.member_id;
28952            this.__sHMemberId = oCache.data.h_member_id;
28953            this.__sName = oCache.data.name;
28954            this.__sNickName = oCache.data.nick_name;
28955            this.__sGroupName = oCache.data.group_name;
28956            this.__sGroupNo = oCache.data.group_no;
28957            this.__sEmail = oCache.data.email;
28958            this.__sNewsMail = oCache.data.news_mail;
28959            this.__sPhone = oCache.data.phone;
28960            this.__sCellphone = oCache.data.cellphone;
28961            this.__sSms = oCache.data.sms;
28962            this.__sBirthday = oCache.data.birthday;
28963            this.__sBoardWriteName = oCache.data.board_write_name;
28964            this.__sAdditionalInformation = oCache.data.additional_information;
28965            this.__sAuthenticationMethod = oCache.data.authentication_method;
28966            this.__sCreatedDate = oCache.data.created_date;
28967
28968            bRestored = true;
28969        } catch (e) {
28970            // 복구 실패시 캐시 삭제
28971            this.removeCache();
28972        }
28973
28974        return bRestored;
28975    },
28976
28977    cache: function()
28978    {
28979        // sessionStorage 지원 여부 확인
28980        if (!window.sessionStorage) {
28981            return;
28982        }
28983        // 캐시
28984        var sData = JSON.stringify({exp: Date.now() + (1000 * 60 * 10),data: this.getData()})
28985
28986        if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
28987            CAFE24.CRYPTOKEY.encryptAndSave('member_' + CAFE24.SDE_SHOP_NUM, sData);
28988        } else if (window.location.protocol === 'http:' && (typeof CryptoJS) == 'object') {
28989            sData = CryptoJS.AES.encrypt(sData, SHOP.getMallID() + '_' + EC_SDE_SHOP_NUM).toString();
28990            window.sessionStorage.setItem('member_' + CAFE24.SDE_SHOP_NUM, sData);
28991        }
28992    },
28993
28994    removeCache: function()
28995    {
28996        // sessionStorage 지원 여부 확인
28997        if (!window.sessionStorage) {
28998            return;
28999        }
29000
29001        // 캐시 삭제
29002        window.sessionStorage.removeItem('encryptedData_member_' + CAFE24.SDE_SHOP_NUM);
29003        window.sessionStorage.removeItem('iv_member_' + CAFE24.SDE_SHOP_NUM);
29004        window.sessionStorage.removeItem('eto4member_' + CAFE24.SDE_SHOP_NUM);
29005        window.sessionStorage.removeItem('eto5member_' + CAFE24.SDE_SHOP_NUM);
29006        window.sessionStorage.removeItem('member_' + CAFE24.SDE_SHOP_NUM);
29007    },
29008
29009    setData: function(oData)
29010    {
29011        this.__sEncryptedString = oData.memberData;
29012        this.__isAdult = oData.memberIsAdult;
29013    },
29014
29015    execute: function()
29016    {
29017        if (this.__sMemberId === null) {
29018            if (this.__sEncryptedString.id) {
29019                this.setDataCallback(this.__sEncryptedString);
29020            } else {
29021                AuthSSLManager.weave({
29022                    'auth_mode': 'decryptClient',
29023                    'auth_string': this.__sEncryptedString,
29024                    'auth_callbackName': 'CAPP_ASYNC_METHODS.member.setDataCallback'
29025                });
29026            }
29027        } else {
29028            this.render();
29029        }
29030    },
29031
29032    setDataCallback: function(sData)
29033    {
29034        try {
29035            if (sData.id) {
29036                var oData = sData;
29037            } else {
29038                var sDecodedData = decodeURIComponent(sData);
29039                if (AuthSSLManager.isError(sDecodedData) == true) {
29040                    console.log(sDecodedData);
29041                    return;
29042                }
29043                var oData = AuthSSLManager.unserialize(sDecodedData);
29044            }
29045
29046            this.__sMemberId = oData.id || '';
29047            this.__sHMemberId = oData.h_member_id || '';
29048            this.__sName = oData.name || '';
29049            this.__sNickName = oData.nick || '';
29050            this.__sGroupName = oData.group_name || '';
29051            this.__sGroupNo = oData.group_no || '';
29052            this.__sEmail = oData.email || '';
29053            this.__sNewsMail = oData.news_mail || '';
29054            this.__sPhone = oData.phone || '';
29055            this.__sCellphone = oData.cellphone || '';
29056            this.__sSms = oData.sms || '';
29057            this.__sBirthday = oData.birthday || 'F';
29058            this.__sBoardWriteName = oData.board_write_name || '';
29059            this.__sAdditionalInformation = oData.additional_information || '';
29060            this.__sAuthenticationMethod = oData.personal_type || null;
29061            this.__sCreatedDate = oData.created_date || '';
29062
29063            // 데이터 랜더링
29064            this.render();
29065
29066            // 데이터 캐시
29067            this.cache();
29068        } catch (e) {}
29069    },
29070
29071    render: function()
29072    {
29073        // 친구초대
29074        if (EC$('.xans-myshop-asyncbenefit').length > 0) {
29075            if (EC$('#reco_url').val() && EC$('#reco_url').val().split('=')[1] == '') {
29076                EC$('#reco_url').attr({value: EC$('#reco_url').val() + this.__sMemberId});
29077            }
29078        }
29079
29080        EC$('.authssl_member_name').html(this.__sName);
29081        EC$('.xans-member-var-id').html(this.__sMemberId);
29082        EC$('.xans-member-var-h_id').html(this.__sHMemberId);
29083        EC$('.xans-member-var-name').html(this.__sName);
29084        EC$('.xans-member-var-nick').html(this.__sNickName);
29085        EC$('.xans-member-var-group_name').html(this.__sGroupName);
29086        EC$('.xans-member-var-group_no').html(this.__sGroupNo);
29087        EC$('.xans-member-var-email').html(this.__sEmail);
29088        EC$('.xans-member-var-phone').html(this.__sPhone);
29089
29090        if (EC$('.xans-board-commentwrite').length > 0 && typeof BOARD_COMMENT !== 'undefined') {
29091            BOARD_COMMENT.setCmtData();
29092        }
29093    },
29094
29095    getMemberIsAdult: function()
29096    {
29097        if (CAPP_ASYNC_METHODS.STATUS == 'unready') {
29098            CAPP_ASYNC_METHODS.init();
29099        }
29100        return this.__isAdult;
29101    },
29102
29103    getData: function()
29104    {
29105        if (CAPP_ASYNC_METHODS.STATUS == 'unready') {
29106            CAPP_ASYNC_METHODS.init();
29107        }
29108        return {
29109            member_id: this.__sMemberId,
29110            h_member_id: this.__sHMemberId,
29111            name: this.__sName,
29112            nick_name: this.__sNickName,
29113            group_name: this.__sGroupName,
29114            group_no: this.__sGroupNo,
29115            email: this.__sEmail,
29116            news_mail: this.__sNewsMail,
29117            phone: this.__sPhone,
29118            cellphone: this.__sCellphone,
29119            sms: this.__sSms,
29120            birthday: this.__sBirthday,
29121            board_write_name: this.__sBoardWriteName,
29122            additional_information: this.__sAdditionalInformation,
29123            authentication_method: this.__sAuthenticationMethod,
29124            created_date: this.__sCreatedDate
29125        };
29126    }
29127};
29128
29129/**
29130 * 비동기식 데이터 - 예치금
29131 */
29132CAPP_ASYNC_METHODS.aDatasetList.push('Ordercnt');
29133CAPP_ASYNC_METHODS.Ordercnt = {
29134    __iOrderShppiedBeforeCount: null,
29135    __iOrderShppiedStandbyCount: null,
29136    __iOrderShppiedBeginCount: null,
29137    __iOrderShppiedComplateCount: null,
29138    __iOrderShppiedCancelCount: null,
29139    __iOrderShppiedExchangeCount: null,
29140    __iOrderShppiedReturnCount: null,
29141
29142    __$target: EC$('#xans_myshop_orderstate_shppied_before_count'),
29143    __$target2: EC$('#xans_myshop_orderstate_shppied_standby_count'),
29144    __$target3: EC$('#xans_myshop_orderstate_shppied_begin_count'),
29145    __$target4: EC$('#xans_myshop_orderstate_shppied_complate_count'),
29146    __$target5: EC$('#xans_myshop_orderstate_order_cancel_count'),
29147    __$target6: EC$('#xans_myshop_orderstate_order_exchange_count'),
29148    __$target7: EC$('#xans_myshop_orderstate_order_return_count'),
29149
29150    isUse: function()
29151    {
29152        if (EC$('.xans-myshop-orderstate').length > 0) {
29153            return true; 
29154        }
29155
29156        return false;
29157    },
29158
29159    restoreCache: function()
29160    {
29161        var sCookieName = 'ordercnt_' + CAFE24.SDE_SHOP_NUM;
29162        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
29163        var aCookieValue = document.cookie.match(re);
29164        if (aCookieValue) {
29165            var aData = CAFE24.UTIL.parseJSON(decodeURIComponent(aCookieValue[1]));
29166            this.__iOrderShppiedBeforeCount = aData.shipped_before_count;
29167            this.__iOrderShppiedStandbyCount = aData.shipped_standby_count;
29168            this.__iOrderShppiedBeginCount = aData.shipped_begin_count;
29169            this.__iOrderShppiedComplateCount = aData.shipped_complate_count;
29170            this.__iOrderShppiedCancelCount = aData.order_cancel_count;
29171            this.__iOrderShppiedExchangeCount = aData.order_exchange_count;
29172            this.__iOrderShppiedReturnCount = aData.order_return_count;
29173            return true;
29174        }
29175
29176        return false;
29177    },
29178
29179    setData: function(aData)
29180    {
29181        this.__iOrderShppiedBeforeCount = aData['shipped_before_count'];
29182        this.__iOrderShppiedStandbyCount = aData['shipped_standby_count'];
29183        this.__iOrderShppiedBeginCount = aData['shipped_begin_count'];
29184        this.__iOrderShppiedComplateCount = aData['shipped_complate_count'];
29185        this.__iOrderShppiedCancelCount = aData['order_cancel_count'];
29186        this.__iOrderShppiedExchangeCount = aData['order_exchange_count'];
29187        this.__iOrderShppiedReturnCount = aData['order_return_count'];
29188    },
29189
29190    execute: function()
29191    {
29192        this.__$target.html(this.__iOrderShppiedBeforeCount);
29193        this.__$target2.html(this.__iOrderShppiedStandbyCount);
29194        this.__$target3.html(this.__iOrderShppiedBeginCount);
29195        this.__$target4.html(this.__iOrderShppiedComplateCount);
29196        this.__$target5.html(this.__iOrderShppiedCancelCount);
29197        this.__$target6.html(this.__iOrderShppiedExchangeCount);
29198        this.__$target7.html(this.__iOrderShppiedReturnCount);
29199    },
29200
29201    getData: function()
29202    {
29203        return {
29204            shipped_before_count: this.__iOrderShppiedBeforeCount,
29205            shipped_standby_count: this.__iOrderShppiedStandbyCount,
29206            shipped_begin_count: this.__iOrderShppiedBeginCount,
29207            shipped_complate_count: this.__iOrderShppiedComplateCount,
29208            order_cancel_count: this.__iOrderShppiedCancelCount,
29209            order_exchange_count: this.__iOrderShppiedExchangeCount,
29210            order_return_count: this.__iOrderShppiedReturnCount
29211        };
29212    }
29213};
29214
29215/**
29216 * 비동기식 데이터 - 장바구니 갯수
29217 */
29218CAPP_ASYNC_METHODS.aDatasetList.push('Basketcnt');
29219CAPP_ASYNC_METHODS.Basketcnt = {
29220    __iBasketCount: null,
29221
29222    __$target: EC$('.xans-layout-orderbasketcount span a'),
29223    __$target2: EC$('#xans_myshop_basket_cnt'),
29224    __$target3: CAPP_ASYNC_METHODS.$xansMyshopMain.find('.xans_myshop_main_basket_cnt'),
29225    __$target4: EC$('.EC-Layout-Basket-count'),
29226
29227    isUse: function()
29228    {
29229        if (this.__$target.length > 0) {
29230            return true;
29231        }
29232        if (this.__$target2.length > 0) {
29233            return true;
29234        }
29235        if (this.__$target3.length > 0) {
29236            return true;
29237        }
29238        if (this.__$target4.length > 0) {
29239            return true;
29240        }
29241
29242        if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('personal', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29243            return true;
29244        }
29245
29246        return false;
29247    },
29248
29249    restoreCache: function()
29250    {
29251        var sCookieName = 'basketcount_' + CAFE24.SDE_SHOP_NUM;
29252
29253        // 쿠키명에 ch_ref 파라미터 추가
29254        if (typeof CAFE24 !== 'undefined' && CAFE24.getChRefData && CAFE24.getChRefData()) {
29255            sCookieName += '_' + CAFE24.getChRefData();
29256        }
29257
29258        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
29259        var aCookieValue = document.cookie.match(re);
29260        if (aCookieValue) {
29261            this.__iBasketCount = parseInt(aCookieValue[1], 10);
29262            return true;
29263        }
29264        
29265        return false;
29266    },
29267
29268    setData: function(sData)
29269    {
29270        this.__iBasketCount = Number(sData);
29271    },
29272
29273    execute: function()
29274    {
29275        this.__$target.html(this.__iBasketCount);
29276
29277        if (SHOP.getLanguage() === 'ko_KR') {
29278            this.__$target2.html(this.__iBasketCount + '개');
29279        } else {
29280            this.__$target2.html(this.__iBasketCount);
29281        }
29282
29283        this.__$target3.html(this.__iBasketCount);
29284        
29285        this.__$target4.html(this.__iBasketCount);
29286        
29287        if (this.__iBasketCount > 0 && this.__$target4.length > 0) {
29288            var $oCountDisplay = EC$('.EC-Layout_Basket-count-display');
29289
29290            if ($oCountDisplay.length > 0) {
29291                $oCountDisplay.removeClass('displaynone');
29292            }
29293        }
29294    },
29295
29296    getData: function()
29297    {
29298        return {
29299            count: this.__iBasketCount
29300        };
29301    }
29302};
29303
29304/**
29305 * 비동기식 데이터 - 장바구니 금액
29306 */
29307CAPP_ASYNC_METHODS.aDatasetList.push('Basketprice');
29308CAPP_ASYNC_METHODS.Basketprice = {
29309    __sBasketPrice: null,
29310
29311    __$target: EC$('#xans_myshop_basket_price'),
29312
29313    isUse: function()
29314    {
29315        if (this.__$target.length > 0) {
29316            return true;
29317        }
29318
29319        if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('personal', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29320            return true;
29321        }
29322
29323        return false;
29324    },
29325
29326    restoreCache: function()
29327    {
29328        var sCookieName = 'basketprice_' + CAFE24.SDE_SHOP_NUM;
29329
29330        // 쿠키명에 ch_ref 파라미터 추가
29331        if (typeof CAFE24 !== 'undefined' && CAFE24.getChRefData && CAFE24.getChRefData()) {
29332            sCookieName += CAFE24.getChRefData();
29333        }
29334
29335        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
29336        var aCookieValue = document.cookie.match(re);
29337        if (aCookieValue) {
29338            this.__sBasketPrice = decodeURIComponent((aCookieValue[1]+ '').replace(/\+/g, '%20'));
29339            return true;
29340        }
29341        
29342        return false;
29343    },
29344
29345    setData: function(sData)
29346    {
29347        this.__sBasketPrice = sData;
29348    },
29349
29350    execute: function()
29351    {
29352        // ECHOSTING-610183: presentment 화폐 변환
29353        var sPrice = this.__sBasketPrice;
29354        var oCurrInfo = CAFE24.SHOP_CURRENCY_INFO ? CAFE24.SHOP_CURRENCY_INFO[CAFE24.SDE_SHOP_NUM] : null;
29355        if (oCurrInfo && oCurrInfo.bPresentmentEnabled && oCurrInfo.aPresentmentCurrencyInfo) {
29356            var fRaw = parseFloat(CAFE24.SHOP_PRICE_FORMAT.detachFormat(htmlentities.decode((sPrice || '0') + '')));
29357            if (!isNaN(fRaw)) {
29358                sPrice = CAFE24.SHOP_PRICE_FORMAT.toShopPricePresentment(fRaw);
29359            }
29360        }
29361        this.__$target.html(sPrice);
29362    },
29363
29364    getData: function()
29365    {
29366        // 데이터 없는경우 0
29367        var sBasketPrice = (this.__sBasketPrice || 0) + '';
29368
29369        return {
29370            basket_price: parseFloat(CAFE24.SHOP_PRICE_FORMAT.detachFormat(htmlentities.decode(sBasketPrice))).toFixed(2)
29371        };
29372    }
29373};
29374
29375/*
29376 * 비동기식 데이터 - 장바구니 상품리스트
29377 */
29378CAPP_ASYNC_METHODS.aDatasetList.push('BasketProduct');
29379CAPP_ASYNC_METHODS.BasketProduct = {
29380
29381    STORAGE_KEY: 'BasketProduct_' + CAFE24.SDE_SHOP_NUM,
29382
29383    __aData: null,
29384
29385    __$target: EC$('.xans-layout-orderbasketcount span a'),
29386    __$target2: EC$('#xans_myshop_basket_cnt'),
29387    __$target3: CAPP_ASYNC_METHODS.$xansMyshopMain.find('.xans_myshop_main_basket_cnt'),
29388    __$target4: EC$('.EC-Layout-Basket-count'),
29389
29390    isUse: function()
29391    {
29392        if (this.__$target.length > 0) {
29393            return true;
29394        }
29395        if (this.__$target2.length > 0) {
29396            return true;
29397        }
29398        if (this.__$target3.length > 0) {
29399            return true;
29400        }
29401        if (this.__$target4.length > 0) {
29402            return true;
29403        }
29404
29405        if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('personal', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29406            return true;
29407        }
29408    },
29409
29410    restoreCache: function()
29411    {
29412        // sessionStorage 지원 여부 확인
29413        if (!window.sessionStorage) {
29414            return false;
29415        }
29416
29417        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
29418        if (sSessionStorageData === null) {
29419            return false;
29420        }
29421
29422        try {
29423            this.__aData = [];
29424            var aStorageData = JSON.parse(sSessionStorageData);
29425
29426            for (var iKey in aStorageData) {
29427                this.__aData.push(aStorageData[iKey]);
29428            }
29429
29430            return true;
29431        } catch (e) {
29432
29433            // 복구 실패시 캐시 삭제
29434            this.removeCache();
29435
29436            return false;
29437        }
29438    },
29439
29440    removeCache: function()
29441    {
29442        // sessionStorage 지원 여부 확인
29443        if (!window.sessionStorage) {
29444            return;
29445        }
29446        // 캐시 삭제
29447        window.sessionStorage.removeItem(this.STORAGE_KEY);
29448    },
29449
29450    setData: function(oData)
29451    {
29452        this.__aData = oData;
29453
29454        // sessionStorage 지원 여부 확인
29455        if (!window.sessionStorage) {
29456            return;
29457        }
29458
29459        try {
29460            sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.getData()));
29461        } catch (error) {
29462        }
29463    },
29464
29465    execute: function()
29466    {
29467
29468    },
29469
29470    getData: function()
29471    {
29472        var aStorageData = this.__aData;
29473        var aResult = [];
29474        return new Promise(function (resolve, reject) {
29475
29476            if (aStorageData != null && aStorageData.length > 1) {
29477                var oNewStorageData = [];
29478
29479                for (var iKey in aStorageData) {
29480                    oNewStorageData.push(aStorageData[iKey]);
29481                }
29482
29483                aResult = oNewStorageData;
29484                resolve(aResult);
29485            } else {
29486                var sUrl = '/exec/front/manage/async?module=BasketProduct';
29487                //랜딩결제 : ch_ref 붙여주기
29488                sUrl = CAFE24.attachChRef(sUrl);
29489                EC$.ajax({
29490                    url: sUrl,
29491                    success: function (aData) {
29492                        aResult = aData.BasketProduct;
29493                        resolve(aResult);
29494                    }
29495                });
29496            }
29497
29498        });
29499    },
29500
29501    setAsyncData: function (aPostData) {
29502        return new Promise(function (resolve, reject) {
29503            EC$.post('/exec/front/order/basket/', aPostData, function (data) {
29504                resolve(data);
29505            }).catch(function (data) {
29506                reject(data);
29507            });
29508        });
29509    },
29510    deleteAllAsyncData: function (basket_shipping_type) {
29511        return new Promise(function (resolve, reject) {
29512            EC$.post('/exec/front/order/basket/', {command: 'delete', delvtype: basket_shipping_type}, function(data) {
29513                resolve(data);
29514            }, 'json').catch(function (data) {
29515                reject(data);
29516            });
29517        });
29518    },
29519
29520    deleteCartItems: function (basket_shipping_type, product_list) {
29521        var aDeleteProducts = [];
29522        var idx = 0;
29523        if (product_list.length > 0) {
29524            for (var iKey in product_list) {
29525                aDeleteProducts[idx] = product_list[iKey].product_no + ':' + product_list[iKey].option_id + ':F:' + product_list[iKey].basket_product_no + ':null:'+basket_shipping_type;
29526                idx ++;
29527            }
29528        }
29529
29530        var sDeleteProducts = aDeleteProducts.join();
29531        return new Promise(function (resolve, reject) {
29532            EC$.post('/exec/front/order/basket/', {command: 'select_delete', checked_product:sDeleteProducts, calls:'CAFE24_SDK'}, function(data) {
29533                CAPP_ASYNC_METHODS.BasketProduct.setData();
29534                resolve(data.aResultDeleteCartItems);
29535            }, 'json').catch(function (data) {
29536                reject(data);
29537            });
29538        });
29539    }
29540};
29541
29542/**
29543 * 비동기식 데이터 - 쿠폰 갯수
29544 */
29545CAPP_ASYNC_METHODS.aDatasetList.push('Couponcnt');
29546CAPP_ASYNC_METHODS.Couponcnt = {
29547    __iCouponCount: null,
29548
29549    __$target: EC$('.xans-layout-myshopcouponcount'),
29550    __$target2: EC$('#xans_myshop_coupon_cnt'),
29551    __$target3: CAPP_ASYNC_METHODS.$xansMyshopMain.find('.xans_myshop_main_coupon_cnt'),
29552    __$target4: EC$('#xans_myshop_bankbook_coupon_cnt'),
29553
29554    isUse: function()
29555    {
29556        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
29557            if (this.__$target.length > 0) {
29558                return true;
29559            }
29560
29561            if (this.__$target2.length > 0) {
29562                return true;
29563            }
29564
29565            if (this.__$target3.length > 0) {
29566                return true;
29567            }
29568
29569            if (this.__$target4.length > 0) {
29570                return true;
29571            }
29572
29573            if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('promotion', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29574                return true;
29575            }
29576        }
29577
29578        return false;
29579    },
29580    
29581    restoreCache: function()
29582    {
29583        var sCookieName = 'couponcount_' + CAFE24.SDE_SHOP_NUM;
29584        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
29585        var aCookieValue = document.cookie.match(re);
29586        if (aCookieValue) {
29587            this.__iCouponCount = parseInt(aCookieValue[1], 10);
29588            return true;
29589        }
29590        
29591        return false;
29592    },
29593    setData: function(sData)
29594    {
29595        this.__iCouponCount = Number(sData);
29596    },
29597
29598    execute: function()
29599    {
29600        this.__$target.html(this.__iCouponCount);
29601
29602        if (SHOP.getLanguage() === 'ko_KR') {
29603            this.__$target2.html(this.__iCouponCount + '개');
29604        } else {
29605            this.__$target2.html(this.__iCouponCount);
29606        }
29607
29608        this.__$target3.html(this.__iCouponCount);
29609        this.__$target4.html(this.__iCouponCount);
29610    },
29611
29612    getData: function()
29613    {
29614        return {
29615            count: this.__iCouponCount
29616        };
29617    }
29618};
29619
29620/**
29621 * 비동기식 데이터 - 적립금
29622 */
29623CAPP_ASYNC_METHODS.aDatasetList.push('Mileage');
29624CAPP_ASYNC_METHODS.Mileage = {
29625    __sAvailMileage: null,
29626    __sUsedMileage: null,
29627    __sTotalMileage: null,
29628    __sUnavailMileage: null,
29629    __sReturnedMileage: null,
29630
29631    __$target: EC$('#xans_myshop_mileage'),
29632    __$target2: EC$('#xans_myshop_bankbook_avail_mileage, #xans_myshop_summary_avail_mileage'),
29633    __$target3: EC$('#xans_myshop_bankbook_used_mileage, #xans_myshop_summary_used_mileage'),
29634    __$target4: EC$('#xans_myshop_bankbook_total_mileage, #xans_myshop_summary_total_mileage'),
29635    __$target5: EC$('#xans_myshop_summary_unavail_mileage'),
29636    __$target6: EC$('#xans_myshop_summary_returned_mileage'),
29637    __$target7: EC$('#xans_myshop_avail_mileage'),
29638
29639    isUse: function()
29640    {
29641        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
29642            if (this.__$target.length > 0) {
29643                return true;
29644            }
29645
29646            if (this.__$target2.length > 0) {
29647                return true;
29648            }
29649
29650            if (this.__$target3.length > 0) {
29651                return true;
29652            }
29653
29654            if (this.__$target4.length > 0) {
29655                return true;
29656            }
29657
29658            if (this.__$target5.length > 0) {
29659                return true;
29660            }
29661
29662            if (this.__$target6.length > 0) {
29663                return true;
29664            }
29665
29666            if (this.__$target7.length > 0) {
29667                return true;
29668            }
29669
29670            if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('customer', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29671                return true;
29672            }
29673        }
29674
29675        return false;
29676    },
29677
29678    restoreCache: function()
29679    {
29680        // 특정 경로 룰의 경우 복구 취소
29681        if (PathRoleValidator.isInvalidPathRole()) {
29682            return false;
29683        }
29684
29685        // 쿠키로부터 데이터 획득
29686        var sAvailMileage = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_avail_mileage_' + CAFE24.SDE_SHOP_NUM);
29687        var sReturnedMileage = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_returned_mileage_' + CAFE24.SDE_SHOP_NUM);
29688        var sUnavailMileage = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_unavail_mileage_' + CAFE24.SDE_SHOP_NUM);
29689        var sUsedMileage = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_used_mileage_' + CAFE24.SDE_SHOP_NUM);
29690
29691        // 데이터가 하나라도 없는경우 복구 실패
29692        if (sAvailMileage === null
29693            || sReturnedMileage === null
29694            || sUnavailMileage === null
29695            || sUsedMileage === null
29696        ) {
29697            return false;
29698        }
29699
29700        // 전체 마일리지 계산
29701        var sTotalMileage = (parseFloat(sAvailMileage) +
29702            parseFloat(sUnavailMileage) +
29703            parseFloat(sUsedMileage)).toString();
29704
29705        // 단위정보를 계산하여 필드에 셋
29706        this.__sAvailMileage = parseFloat(sAvailMileage).toFixed(2);
29707        this.__sReturnedMileage = parseFloat(sReturnedMileage).toFixed(2);
29708        this.__sUnavailMileage = parseFloat(sUnavailMileage).toFixed(2);
29709        this.__sUsedMileage = parseFloat(sUsedMileage).toFixed(2);
29710        this.__sTotalMileage = parseFloat(sTotalMileage).toFixed(2);
29711
29712        return true;
29713    },
29714
29715    setData: function(aData)
29716    {
29717        this.__sAvailMileage = parseFloat(aData['avail_mileage'] || 0).toFixed(2);
29718        this.__sUsedMileage = parseFloat(aData['used_mileage'] || 0).toFixed(2);
29719        this.__sTotalMileage = parseFloat(aData['total_mileage'] || 0).toFixed(2);
29720        this.__sUnavailMileage = parseFloat(aData['unavail_mileage'] || 0).toFixed(2);
29721        this.__sReturnedMileage = parseFloat(aData['returned_mileage'] || 0).toFixed(2);
29722    },
29723
29724    execute: function()
29725    {
29726        // ECHOSTING-610183: presentment 화폐 변환
29727        var fnFormat = CAFE24.SHOP_PRICE_FORMAT.toShopMileagePrice;
29728        var oCurrInfo = CAFE24.SHOP_CURRENCY_INFO ? CAFE24.SHOP_CURRENCY_INFO[CAFE24.SDE_SHOP_NUM] : null;
29729        if (oCurrInfo && oCurrInfo.bPresentmentEnabled && oCurrInfo.aPresentmentCurrencyInfo) {
29730            fnFormat = CAFE24.SHOP_PRICE_FORMAT.toShopMileagePricePresentment;
29731        }
29732        this.__$target.html(fnFormat(this.__sAvailMileage));
29733        this.__$target2.html(fnFormat(this.__sAvailMileage));
29734        this.__$target3.html(fnFormat(this.__sUsedMileage));
29735        this.__$target4.html(fnFormat(this.__sTotalMileage));
29736        this.__$target5.html(fnFormat(this.__sUnavailMileage));
29737        this.__$target6.html(fnFormat(this.__sReturnedMileage));
29738        this.__$target7.html(fnFormat(this.__sAvailMileage));
29739    },
29740
29741    getData: function()
29742    {
29743        return {
29744            available_mileage: this.__sAvailMileage,
29745            used_mileage: this.__sUsedMileage,
29746            total_mileage: this.__sTotalMileage,
29747            returned_mileage: this.__sReturnedMileage,
29748            unavailable_mileage: this.__sUnavailMileage
29749        };
29750    }
29751};
29752
29753/**
29754 * 비동기식 데이터 - 예치금
29755 */
29756CAPP_ASYNC_METHODS.aDatasetList.push('Deposit');
29757CAPP_ASYNC_METHODS.Deposit = {
29758    __sTotalDeposit: null,
29759    __sAllDeposit: null,
29760    __sUsedDeposit: null,
29761    __sRefundWaitDeposit: null,
29762    __sMemberTotalDeposit: null,
29763
29764    __$target: EC$('#xans_myshop_deposit'),
29765    __$target2: EC$('#xans_myshop_bankbook_deposit'),
29766    __$target3: EC$('#xans_myshop_summary_deposit'),
29767    __$target4: EC$('#xans_myshop_summary_all_deposit'),
29768    __$target5: EC$('#xans_myshop_summary_used_deposit'),
29769    __$target6: EC$('#xans_myshop_summary_refund_wait_deposit'),
29770    __$target7: EC$('#xans_myshop_total_deposit'),
29771
29772    isUse: function()
29773    {
29774        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
29775            if (this.__$target.length > 0) {
29776                return true;
29777            }
29778
29779            if (this.__$target2.length > 0) {
29780                return true;
29781            }
29782
29783            if (this.__$target3.length > 0) {
29784                return true;
29785            }
29786
29787            if (this.__$target4.length > 0) {
29788                return true;
29789            }
29790
29791            if (this.__$target5.length > 0) {
29792                return true;
29793            }
29794
29795            if (this.__$target6.length > 0) {
29796                return true;
29797            }
29798
29799            if (this.__$target7.length > 0) {
29800                return true;
29801            }
29802
29803            if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('customer', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
29804                return true;
29805            }
29806        }
29807
29808        return false;
29809    },
29810
29811    restoreCache: function()
29812    {
29813        // 특정 경로 룰의 경우 복구 취소
29814        if (PathRoleValidator.isInvalidPathRole()) {
29815            return false;
29816        }
29817
29818        // 쿠키로부터 데이터 획득
29819        var sAllDeposit = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_all_deposit_' + CAFE24.SDE_SHOP_NUM);
29820        var sUsedDeposit = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_used_deposit_' + CAFE24.SDE_SHOP_NUM);
29821        var sRefundWaitDeposit = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_deposit_refund_wait_' + CAFE24.SDE_SHOP_NUM);
29822        var sMemberTotalDeposit = CAPP_ASYNC_METHODS._getCookie('ec_async_cache_member_total_deposit_' + CAFE24.SDE_SHOP_NUM);
29823
29824        // 데이터가 하나라도 없는경우 복구 실패
29825        if (sAllDeposit === null
29826            || sUsedDeposit === null
29827            || sRefundWaitDeposit === null
29828            || sMemberTotalDeposit === null
29829        ) {
29830            return false;
29831        }
29832
29833        // 사용 가능한 예치금 계산
29834        var sTotalDeposit = (parseFloat(sAllDeposit) -
29835            parseFloat(sUsedDeposit) -
29836            parseFloat(sRefundWaitDeposit)).toString();
29837
29838        // 단위정보를 계산하여 필드에 셋
29839        this.__sTotalDeposit = parseFloat(sTotalDeposit).toFixed(2);
29840        this.__sAllDeposit = parseFloat(sAllDeposit).toFixed(2);
29841        this.__sUsedDeposit = parseFloat(sUsedDeposit).toFixed(2);
29842        this.__sRefundWaitDeposit = parseFloat(sRefundWaitDeposit).toFixed(2);
29843        this.__sMemberTotalDeposit = parseFloat(sMemberTotalDeposit).toFixed(2);
29844
29845        return true;
29846    },
29847
29848    setData: function(aData)
29849    {
29850        this.__sTotalDeposit = parseFloat(aData['total_deposit'] || 0).toFixed(2);
29851        this.__sAllDeposit = parseFloat(aData['all_deposit'] || 0).toFixed(2);
29852        this.__sUsedDeposit = parseFloat(aData['used_deposit'] || 0).toFixed(2);
29853        this.__sRefundWaitDeposit = parseFloat(aData['deposit_refund_wait'] || 0).toFixed(2);
29854        this.__sMemberTotalDeposit = parseFloat(aData['member_total_deposit'] || 0).toFixed(2);
29855    },
29856
29857    execute: function()
29858    {
29859        // ECHOSTING-616370: presentment 화폐 변환
29860        var fnFormat = CAFE24.SHOP_PRICE_FORMAT.toShopDepositPrice;
29861        var oCurrInfo = CAFE24.SHOP_CURRENCY_INFO ? CAFE24.SHOP_CURRENCY_INFO[CAFE24.SDE_SHOP_NUM] : null;
29862        if (oCurrInfo && oCurrInfo.bPresentmentEnabled && oCurrInfo.aPresentmentCurrencyInfo) {
29863            fnFormat = CAFE24.SHOP_PRICE_FORMAT.toShopDepositPricePresentment;
29864        }
29865        this.__$target.html(fnFormat(this.__sTotalDeposit));
29866        this.__$target2.html(fnFormat(this.__sTotalDeposit));
29867        this.__$target3.html(fnFormat(this.__sTotalDeposit));
29868        this.__$target4.html(fnFormat(this.__sAllDeposit));
29869        this.__$target5.html(fnFormat(this.__sUsedDeposit));
29870        this.__$target6.html(fnFormat(this.__sRefundWaitDeposit));
29871        this.__$target7.html(fnFormat(this.__sMemberTotalDeposit));
29872    },
29873
29874    getData: function()
29875    {
29876        return {
29877            total_deposit: this.__sTotalDeposit,
29878            used_deposit: this.__sUsedDeposit,
29879            refund_wait_deposit: this.__sRefundWaitDeposit,
29880            all_deposit: this.__sAllDeposit,
29881            member_total_deposit: this.__sMemberTotalDeposit
29882        };
29883    }
29884};
29885
29886/**
29887 * 비동기식 데이터 - 위시리스트
29888 */
29889CAPP_ASYNC_METHODS.aDatasetList.push('WishList');
29890CAPP_ASYNC_METHODS.WishList = {
29891    STORAGE_KEY: 'localWishList' + CAFE24.SDE_SHOP_NUM,
29892    __$targetWishIcon: EC$('.icon_img.ec-product-listwishicon'),
29893    __$targetWishList: EC$('.xans-myshop-wishlist'),
29894    __aWishList: null,
29895    __aTags_on: null,
29896    __aTags_off: null,
29897
29898    isUse: function()
29899    {
29900        if (this.__$targetWishIcon.length > 0 || this.__$targetWishList.length > 0
29901        || CAPP_ASYNC_METHODS.EC_PATH_ROLE === 'PRODUCT_DETAIL') {
29902            return true;
29903        }
29904        return false;
29905    },
29906
29907    restoreCache: function()
29908    {
29909        if (!window.sessionStorage) {
29910            return false;
29911        }
29912
29913        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
29914        if (sSessionStorageData === null) {
29915            return false;
29916        }
29917
29918        var aStorageData = CAFE24.UTIL.parseJSON(sSessionStorageData);
29919        if (this.__$targetWishList.length > 0 || aStorageData['isLogin'] !== CAPP_ASYNC_METHODS.IS_LOGIN) {
29920            this.clearStorage();
29921            return false;
29922        }
29923
29924        var aWishList = aStorageData['wishList'];
29925        this.__aTags_on = aStorageData['on_tags'];
29926        this.__aTags_off = aStorageData['off_tags'];
29927        this.__aWishList = [];
29928        for (var i = 0; i < aWishList.length; i++) {
29929            var aTempWishList = [];
29930            aTempWishList.product_no = aWishList[i];
29931            this.__aWishList.push(aTempWishList);
29932        }
29933        return true;
29934    },
29935
29936    setData: function(aData)
29937    {
29938        if (aData.hasOwnProperty('wishList') === false || aData.hasOwnProperty('on_tags') === false) {
29939            return;
29940        }
29941
29942        this.__aWishList = aData.wishList;
29943        this.__aTags_on = aData.on_tags;
29944        this.__aTags_off = aData.off_tags;
29945
29946        if (window.sessionStorage) {
29947            var aWishList = [];
29948
29949            for (var i = 0; i < aData.wishList.length; i++) {
29950                aWishList.push(aData.wishList[i].product_no);
29951            }
29952
29953            var oNewStorageData = {
29954                'wishList': aWishList,
29955                'on_tags': aData.on_tags,
29956                'off_tags': aData.off_tags,
29957                'isLogin': CAPP_ASYNC_METHODS.IS_LOGIN
29958            };
29959
29960            if (typeof oNewStorageData !== 'undefined') {
29961                sessionStorage.setItem(this.STORAGE_KEY , JSON.stringify(oNewStorageData));
29962            }
29963        }
29964    },
29965
29966    execute: function()
29967    {
29968        var aWishList = this.__aWishList;
29969        var aTagsOn = this.__aTags_on;
29970        var aTagsOff = this.__aTags_off;
29971
29972        if (aWishList === null || typeof aWishList === 'undefined') {
29973            aWishList = [];
29974        }
29975
29976        var oTarget = EC$('.ec-product-listwishicon');
29977        for (var sKey in aTagsOff) {
29978            oTarget.attr(sKey, aTagsOff[sKey]);
29979        }
29980
29981        for (var i = 0; i < aWishList.length; i++) {
29982            assignAttribute(aWishList[i]);
29983        }
29984
29985        /**
29986         * oTarget 엘레먼트에 aData의 정보를 어싸인함.
29987         * @param array aData 위시리스트 정보
29988         */
29989        function assignAttribute(aData)
29990        {
29991            var iProductNo = aData['product_no'];
29992            var oTarget = EC$('.ec-product-listwishicon[productno="'+iProductNo+'"]');
29993
29994            // oTarget의 src, alt, icon_status attribute의 값을 할당
29995            for (var sKey in aTagsOn) {
29996                oTarget.attr(sKey, aTagsOn[sKey]);
29997            }
29998        }
29999
30000    },
30001
30002    /**
30003     * 세션스토리지 삭제
30004     */
30005    clearStorage: function()
30006    {
30007        if (!window.sessionStorage) {
30008            return;
30009        }
30010        window.sessionStorage.removeItem(this.STORAGE_KEY);
30011    },
30012
30013    /**
30014     * sCommand에 따른 sessionStorage Set
30015     * @param iProductNo
30016     * @param sCommand 추가(add)/삭제(del) sCommand
30017     */
30018    setSessionStorageItem: function(iProductNo, sCommand)
30019    {
30020        if (this.isUse() === false) {
30021            return;
30022        }
30023
30024        var oStorageData = CAFE24.UTIL.parseJSON(sessionStorage.getItem(this.STORAGE_KEY));
30025        var aWishList = oStorageData['wishList'];
30026        var iLimit = 200;
30027
30028        if (aWishList === null) {
30029            aWishList = [];
30030        }
30031
30032        var iProductNo = parseInt(iProductNo, 10);
30033        var iIndex = aWishList.indexOf(iProductNo);
30034
30035        if (sCommand === 'add') {
30036            if (aWishList.length >= iLimit) {
30037                aWishList.splice(aWishList.length - 1, 1);
30038            }
30039            if (iIndex < 0) {
30040                aWishList.unshift(iProductNo);
30041            }
30042        } else {
30043            if (iIndex > -1) {
30044                aWishList.splice(iIndex, 1);
30045            }
30046        }
30047
30048        oStorageData['wishList'] = aWishList;
30049        sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(oStorageData));
30050    }
30051};
30052
30053/**
30054 * 비동기식 데이터 - 관심상품 갯수
30055 */
30056CAPP_ASYNC_METHODS.aDatasetList.push('Wishcount');
30057CAPP_ASYNC_METHODS.Wishcount = {
30058    __iWishCount: null,
30059
30060    __$target: EC$('#xans_myshop_interest_prd_cnt'),
30061    __$target2: CAPP_ASYNC_METHODS.$xansMyshopMain.find('.xans_myshop_main_interest_prd_cnt'),
30062
30063    isUse: function()
30064    {
30065        if (this.__$target.length > 0) {
30066            return true;
30067        }
30068        if (this.__$target2.length > 0) {
30069            return true;
30070        }
30071
30072        if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('personal', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
30073            return true;
30074        }
30075
30076        return false;
30077    },
30078
30079    restoreCache: function()
30080    {
30081        var sCookieName = 'wishcount_' + CAFE24.SDE_SHOP_NUM;
30082        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
30083        var aCookieValue = document.cookie.match(re);
30084        if (aCookieValue) {
30085            this.__iWishCount = parseInt(aCookieValue[1], 10);
30086            return true;
30087        }
30088
30089        return false;
30090    },
30091
30092    setData: function(sData)
30093    {
30094        this.__iWishCount = Number(sData);
30095    },
30096
30097    execute: function()
30098    {
30099        if (SHOP.getLanguage() === 'ko_KR') {
30100            this.__$target.html(this.__iWishCount + '개');
30101        } else {
30102            this.__$target.html(this.__iWishCount);
30103        }
30104
30105        this.__$target2.html(this.__iWishCount);
30106    },
30107
30108    getData: function()
30109    {
30110        return {
30111            count: this.__iWishCount
30112        };
30113    }
30114};
30115
30116/**
30117 * 비동기식 데이터 - 최근 본 상품
30118 */
30119CAPP_ASYNC_METHODS.aDatasetList.push('recent');
30120CAPP_ASYNC_METHODS.recent = {
30121    STORAGE_KEY: 'localRecentProduct' + CAFE24.SDE_SHOP_NUM,
30122
30123    __$target: EC$('.xans-layout-productrecent'),
30124
30125    __aData: null,
30126
30127    isUse: function()
30128    {
30129        this.__$target.hide();
30130
30131        if (this.__$target.find('.xans-record-').length > 0) {
30132            return true;
30133        }
30134
30135        return false;
30136    },
30137
30138    restoreCache: function()
30139    {
30140        this.__aData = [];
30141
30142        var iTotalCount = CAPP_ASYNC_METHODS.RecentTotalCount.getData();
30143        if (iTotalCount == 0) {
30144            // 총 갯수가 없는 경우 복구할 것이 없으므로 복구한 것으로 리턴
30145            return true;
30146        }
30147
30148        var sAdultImage = '';
30149
30150        if (window.sessionStorage === undefined) {
30151            return false;
30152        }
30153
30154        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
30155        if (sSessionStorageData === null) {
30156            return false;
30157        }
30158
30159        var iViewCount = EC_FRONT_JS_CONFIG_SHOP.recent_count;
30160
30161        this.__aData = [];
30162        var aStorageData = CAFE24.UTIL.parseJSON(sSessionStorageData);
30163        var iCount = 1;
30164        var bDispRecent = true;
30165        for (var iKey in aStorageData) {
30166            var sProductImgSrc = aStorageData[iKey].sImgSrc;
30167
30168            if (isFinite(iKey) === false) {
30169                continue;
30170            }
30171
30172            var aDataTmp = [];
30173            aDataTmp.recent_img = getImageUrl(sProductImgSrc);
30174            aDataTmp.name = aStorageData[iKey].sProductName;
30175            aDataTmp.disp_recent = true;
30176            aDataTmp.is_adult_product = aStorageData[iKey].isAdultProduct;
30177            aDataTmp.link_product_detail = aStorageData[iKey].link_product_detail;
30178
30179            //aDataTmp.param = '?product_no=' + aStorageData[iKey].iProductNo + '&cate_no=' + aStorageData[iKey].iCateNum + '&display_group=' + aStorageData[iKey].iDisplayGroup;
30180            aDataTmp.param = filterXssUrlParameter(aStorageData[iKey].sParam);
30181            if (iViewCount < iCount) {
30182                bDispRecent = false;
30183            }
30184            aDataTmp.disp_recent = bDispRecent;
30185
30186            iCount++;
30187            this.__aData.push(aDataTmp);
30188        }
30189
30190        return true;
30191
30192        /**
30193         * get SessionStorage image url
30194         * @param sNewImgUrl DB에 저장되어 있는 tiny값
30195         */
30196        function getImageUrl(sImgUrl)
30197        {
30198            if (typeof(sImgUrl) === 'undefined' || sImgUrl === null) {
30199                return;
30200            }
30201            var sNewImgUrl = '';
30202
30203            if (sImgUrl.indexOf('http://') >= 0 || sImgUrl.indexOf('https://') >= 0 || sImgUrl.substr(0, 2) === '//') {
30204                sNewImgUrl = sImgUrl;
30205            } else {
30206                sNewImgUrl = EC_FRONT_JS_CONFIG_SHOP.cdnUrl + '/web/product/tiny/' + sImgUrl;
30207            }
30208
30209            return sNewImgUrl;
30210        }
30211
30212        /**
30213         * 파라미터 URL에서 XSS 공격 관련 파라미터를 필터링합니다. ECHOSTING-162977
30214         * @param string sParam 파라미터
30215         * @return string 필터링된 파라미터
30216         */
30217        function filterXssUrlParameter(sParam)
30218        {
30219            sParam = sParam || '';
30220
30221            var sPrefix = '';
30222            if (sParam.substr(0, 1) === '?') {
30223                sPrefix = '?';
30224                sParam = sParam.substr(1);
30225            }
30226
30227            var aParam = {};
30228
30229            var aParamList = (sParam).split('&');
30230            EC$.each(aParamList, function() {
30231                var aMatch = this.match(/^([^=]+)=(.*)$/);
30232                if (aMatch) {
30233                    aParam[aMatch[1]] = aMatch[2];
30234                }
30235            });
30236
30237            return sPrefix + EC$.param(aParam);
30238        }
30239
30240    },
30241
30242    setData: function(aData)
30243    {
30244        this.__aData = aData;
30245
30246        // 쿠키엔 있지만 sessionStorage에 없는 데이터 복구
30247        if (window.sessionStorage) {
30248
30249            var oNewStorageData = [];
30250
30251            for (var i = 0; i < aData.length; i++) {
30252                if (aData[i].bNewProduct !== true) {
30253                    continue;
30254                }
30255
30256                var aNewStorageData = {
30257                    'iProductNo': aData[i].product_no,
30258                    'sProductName': aData[i].name,
30259                    'sImgSrc': aData[i].recent_img,
30260                    'sParam': aData[i].param,
30261                    'link_product_detail': aData[i].link_product_detail
30262                };
30263
30264                oNewStorageData.push(aNewStorageData);
30265            }
30266
30267            if (oNewStorageData.length > 0) {
30268                sessionStorage.setItem(this.STORAGE_KEY , JSON.stringify(oNewStorageData));
30269            }
30270        }
30271    },
30272
30273    execute: function()
30274    {
30275        var sAdult19Warning = EC_FRONT_JS_CONFIG_SHOP.adult19Warning;
30276
30277        var aData = this.__aData;
30278
30279        var aNodes = this.__$target.find('.xans-record-');
30280        var iRecordCnt = aNodes.length;
30281        var iAddedElementCount = 0;
30282
30283        var aNodesParent = EC$(aNodes[0]).parent();
30284        for (var i = 0; i < aData.length; i++) {
30285            if (!aNodes[i]) {
30286                EC$(aNodes[iRecordCnt - 1]).clone().appendTo(aNodesParent);
30287                iAddedElementCount++;
30288            }
30289        }
30290
30291        if (iAddedElementCount > 0) {
30292            aNodes = this.__$target.find('.xans-record-');
30293        }
30294
30295        if (aData.length > 0) {
30296            this.__$target.show();
30297        }
30298
30299        for (var i = 0; i < aData.length; i++) {
30300            assignVariables(aNodes[i], aData[i]);
30301        }
30302
30303        // 종료 카운트 지정
30304        if (aData.length < aNodes.length) {
30305            iLength = aData.length;
30306            deleteNode();
30307        }
30308
30309        recentBntInit(this.__$target);
30310
30311        /**
30312         * 패치되지 않은 노드를 제거
30313         */
30314        function deleteNode()
30315        {
30316            for (var i = iLength; i < aNodes.length; i++) {
30317                EC$(aNodes[i]).remove();
30318            }
30319        }
30320
30321        /**
30322         * oTarget 엘레먼트에 aData의 변수를 어싸인합니다.
30323         * @param Element oTarget 변수를 어싸인할 엘레먼트
30324         * @param array aData 변수 데이터
30325         */
30326        function assignVariables(oTarget, aData)
30327        {
30328            var recentImage = aData.recent_img;
30329
30330            if (sAdult19Warning === 'T' && CAPP_ASYNC_METHODS.member.getMemberIsAdult() === 'F' && aData.is_adult_product === 'T') {
30331                    recentImage = EC_FRONT_JS_CONFIG_SHOP.adult19BaseTinyImage;
30332            }
30333
30334            var $oTarget = EC$(oTarget);
30335
30336            var sHtml = $oTarget.html();
30337
30338            sHtml = sHtml.replace('about:blank', recentImage)
30339                         .replace('##param##', aData.param)
30340                         .replace('##name##',aData.name)
30341                         .replace('##link_product_detail##', aData.link_product_detail);
30342            $oTarget.html(sHtml);
30343
30344            if (aData.disp_recent === true) {
30345                $oTarget.removeClass('displaynone');
30346            }
30347        }
30348
30349        function recentBntInit($target)
30350        {
30351            // 화면에 뿌려진 갯수
30352            var iDisplayCount = 0;
30353            // 보여지는 style
30354            var sDisplay = '';
30355            var iIdx = 0;
30356            //
30357            var iDisplayNoneIdx = 0;
30358
30359            var nodes = $target.find('.xans-record-').each(function()
30360            {
30361                sDisplay = EC$(this).css('display');
30362                if (sDisplay != 'none') {
30363                    iDisplayCount++;
30364                } else {
30365                    if (iDisplayNoneIdx == 0) {
30366                        iDisplayNoneIdx = iIdx;
30367                    }
30368
30369                }
30370                iIdx++;
30371            });
30372
30373            var iRecentCount = nodes.length;
30374            var bBtnActive = iDisplayCount > 0;
30375            EC$('.xans-layout-productrecent .prev').off('click').click(function()
30376            {
30377                if (bBtnActive !== true) return;
30378                var iFirstNode = iDisplayNoneIdx - iDisplayCount;
30379                if (iFirstNode == 0 || iDisplayCount == iRecentCount) {
30380                    alert(__('최근 본 첫번째 상품입니다.'));
30381                    return;
30382                } else {
30383                    iDisplayNoneIdx--;
30384                    EC$(nodes[iDisplayNoneIdx]).hide();
30385                    EC$(nodes[iFirstNode - 1]).removeClass('displaynone');
30386                    EC$(nodes[iFirstNode - 1]).fadeIn('fast');
30387
30388                }
30389            }).css(
30390            {
30391                cursor: 'pointer'
30392            });
30393
30394            EC$('.xans-layout-productrecent .next').off('click').click(function()
30395            {
30396                if (bBtnActive !== true) return;
30397                if ((iRecentCount) == iDisplayNoneIdx || iDisplayCount == iRecentCount) {
30398                    alert(__('최근 본 마지막 상품입니다.'));
30399                } else {
30400                    EC$(nodes[iDisplayNoneIdx]).fadeIn('fast');
30401                    EC$(nodes[iDisplayNoneIdx]).removeClass('displaynone');
30402                    EC$(nodes[ (iDisplayNoneIdx - iDisplayCount)]).hide();
30403                    iDisplayNoneIdx++;
30404                }
30405            }).css(
30406            {
30407                cursor: 'pointer'
30408            });
30409
30410        }
30411
30412    }
30413};
30414
30415/**
30416 * 비동기식 데이터 - Recentkeyword
30417 */
30418CAPP_ASYNC_METHODS.aDatasetList.push('Recentkeyword');
30419CAPP_ASYNC_METHODS.Recentkeyword = {
30420    __$target: EC$('div.xans-search-recentkeyword'),
30421    RECENT_WORD_KEY: 'RECENT_WORD_' + EC_SDE_SHOP_NUM + '_',
30422    RECENT_WORD_COUNT: 10,
30423
30424    isUse: function()
30425    {
30426        if (this.__$target.length > 0) {
30427            return true;
30428        }
30429    },
30430    //restoreCache: function()
30431    // {
30432    //     var sCookieName = 'RECENT_WORD_' + CAFE24.SDE_SHOP_NUM;
30433    //     var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
30434    //     var aCookieValue = document.cookie.match(re);
30435    //     if (aCookieValue) {
30436    //         this.recentword = aCookieValue[1];
30437    //         return true;
30438    //     }
30439    //
30440    //     return false;
30441    // },
30442
30443    setData: function()
30444    {
30445    },
30446    execute: function()
30447    {
30448        var oTemplate = this.getTemplate();
30449        var sHtml = '';
30450        var sRecentSearchUrl = '/product/search.html?keyword=';
30451        if (typeof(EC_FRONT_JS_CONFIG_SHOP.sSearchUrl) !== 'undefined') {
30452            sRecentSearchUrl = EC_FRONT_JS_CONFIG_SHOP.sSearchUrl;
30453        }
30454
30455        for (var i=1; i <= this.RECENT_WORD_COUNT; i++) {
30456            var sRecentKeyword = CAPP_ASYNC_METHODS._getCookie(this.RECENT_WORD_KEY + i);
30457
30458            if (sRecentKeyword !== null) {
30459                sRecentKeyword = decodeURIComponent(sRecentKeyword);
30460                var oLi = oTemplate.clone();
30461                oLi.find('a').attr('href', sRecentSearchUrl + sRecentKeyword);
30462                oLi.find('a').text(sRecentKeyword);
30463                oLi.find('button').addClass('recent_keyword_remove');
30464                oLi.find('button').attr('index', i);
30465                oLi.find('button').removeAttr('onclick');
30466                sHtml += '<li data-index="' + i + '">' + oLi.html() + '</li>' ;
30467
30468            }
30469        }
30470
30471        CAPP_ASYNC_METHODS.Recentkeyword.__$target.find('ul').html(sHtml);
30472        CAPP_ASYNC_METHODS.Recentkeyword.__$target.find('button.btnDeleteAll').addClass('recent_keyword_remove_all').removeAttr('onclick');
30473    },
30474    getTemplate: function ()
30475    {
30476        return this.__$target.find('ul > li:eq(0)').clone();
30477
30478    }
30479}
30480/**
30481 * 비동기식 데이터 - 최근본상품 총 갯수
30482 */
30483CAPP_ASYNC_METHODS.aDatasetList.push('RecentTotalCount');
30484CAPP_ASYNC_METHODS.RecentTotalCount = {
30485    __iRecentCount: null,
30486
30487    __$target: CAPP_ASYNC_METHODS.$xansMyshopMain.find('.xans_myshop_main_recent_cnt'),
30488
30489    isUse: function()
30490    {
30491        if (this.__$target.length > 0) {
30492            return true;
30493        }
30494
30495        return false;
30496    },
30497
30498    restoreCache: function()
30499    {
30500        var sCookieName = 'recent_plist';
30501        if (CAFE24.SDE_SHOP_NUM > 1) {
30502            sCookieName = 'recent_plist' + CAFE24.SDE_SHOP_NUM;
30503        }
30504        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
30505        var aCookieValue = document.cookie.match(re);
30506        if (aCookieValue) {
30507            this.__iRecentCount = decodeURI(aCookieValue[1]).split('|').length;
30508        } else {
30509            this.__iRecentCount = 0;
30510        }
30511    },
30512
30513    execute: function()
30514    {
30515        this.__$target.html(this.__iRecentCount);
30516    },
30517
30518    getData: function()
30519    {
30520        if (this.__iRecentCount === null) {
30521            // this.isUse값이 false라서 복구되지 않았는데 이 값이 필요한 경우 복구
30522            this.restoreCache();
30523        }
30524
30525        return this.__iRecentCount;
30526    }
30527};
30528
30529/**
30530 * 비동기식 데이터 - 주문정보
30531 */
30532CAPP_ASYNC_METHODS.aDatasetList.push('Order');
30533CAPP_ASYNC_METHODS.Order = {
30534    __iOrderCount: null,
30535    __iOrderTotalPrice: null,
30536    __iGradeIncreaseValue: null,
30537
30538    __$target: EC$('#xans_myshop_bankbook_order_count'),
30539    __$target2: EC$('#xans_myshop_bankbook_order_price'),
30540    __$target3: EC$('#xans_myshop_bankbook_grade_increase_value'),
30541
30542    isUse: function()
30543    {
30544        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
30545            if (this.__$target.length > 0) {
30546                return true;
30547            }
30548
30549            if (this.__$target2.length > 0) {
30550                return true;
30551            }
30552
30553            if (this.__$target3.length > 0) {
30554                return true;
30555            }
30556        }
30557        
30558        return false;        
30559    },
30560
30561    restoreCache: function()
30562    {
30563        var sCookieName = 'order_' + CAFE24.SDE_SHOP_NUM;
30564        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
30565        var aCookieValue = document.cookie.match(re);
30566        if (aCookieValue) {
30567            var aData = CAFE24.UTIL.parseJSON(decodeURIComponent(aCookieValue[1]));
30568            this.__iOrderCount = aData.total_order_count;
30569            this.__iOrderTotalPrice = aData.total_order_price;
30570            this.__iGradeIncreaseValue = Number(aData.grade_increase_value);
30571            return true;
30572        }
30573
30574        return false;
30575    },
30576
30577    setData: function(aData)
30578    {
30579        this.__iOrderCount = aData['total_order_count'];
30580        this.__iOrderTotalPrice = aData['total_order_price'];
30581        this.__iGradeIncreaseValue = Number(aData['grade_increase_value']);
30582    },
30583
30584    execute: function()
30585    {
30586        this.__$target.html(this.__iOrderCount);
30587        this.__$target2.html(this.__iOrderTotalPrice);
30588        this.__$target3.html(this.__iGradeIncreaseValue);
30589    },
30590
30591    getData: function()
30592    {
30593        return {
30594            total_order_count: this.__iOrderCount,
30595            total_order_price: this.__iOrderTotalPrice,
30596            grade_increase_value: this.__iGradeIncreaseValue
30597        };
30598    }
30599};
30600
30601/**
30602 * 비동기식 데이터 - Benefit
30603 */
30604CAPP_ASYNC_METHODS.aDatasetList.push('Benefit');
30605CAPP_ASYNC_METHODS.Benefit = {
30606    __aBenefit: null,
30607    __$target: EC$('.xans-myshop-asyncbenefit'),
30608
30609    isUse: function()
30610    {
30611        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
30612            if (this.__$target.length > 0) {
30613                return true;
30614            }
30615        }
30616
30617        return false;
30618    },
30619
30620    setData: function(aData)
30621    {
30622        this.__aBenefit = aData;
30623    },
30624
30625    execute: function()
30626    {
30627        var aFilter = [
30628            'group_image_tag',
30629            'group_icon_tag',
30630            'display_no_benefit',
30631            'display_with_all',
30632            'display_mobile_use_dc',
30633            'display_mobile_use_mileage',
30634            'display_member_group_dc_limit'
30635        ];
30636        var __aData = this.__aBenefit;
30637        
30638        // 그룹이미지
30639        EC$('.myshop_benefit_group_image_tag').attr({alt: __aData['group_name'], src: __aData['group_image']});
30640
30641        // 그룹아이콘
30642        EC$('.myshop_benefit_group_icon_tag').attr({alt: __aData['group_name'], src: __aData['group_icon']});
30643
30644        if (__aData['display_no_benefit'] === true) {
30645            EC$('.myshop_benefit_display_no_benefit').removeClass('displaynone').show();
30646        }
30647        
30648        if (__aData['display_with_all'] === true) {
30649            EC$('.myshop_benefit_display_with_all').removeClass('displaynone').show();
30650        }
30651        
30652        if (__aData['display_mobile_use_dc'] === true) {
30653            EC$('.myshop_benefit_display_mobile_use_dc').removeClass('displaynone').show();
30654        } 
30655        
30656        if (__aData['display_mobile_use_mileage'] === true) {
30657            EC$('.myshop_benefit_display_mobile_use_mileage').removeClass('displaynone').show();
30658        }
30659
30660        if (__aData['display_member_group_dc_limit'] === true) {
30661            EC$('.myshop_benefit_display_member_group_dc_limit').removeClass('displaynone').show();
30662        }
30663
30664        EC$.each(__aData, function(key, val) {
30665            if (EC$.inArray(key, aFilter) === -1) {
30666                EC$('.myshop_benefit_' + key).html(val);
30667            }
30668        });
30669    }    
30670};
30671
30672/**
30673 * 비동기식 데이터 - 비동기장바구니 레이어
30674 */
30675CAPP_ASYNC_METHODS.aDatasetList.push('BasketLayer');
30676CAPP_ASYNC_METHODS.BasketLayer = {
30677    __sBasketLayerHtml: null,
30678    __$target: document.getElementById('ec_async_basket_layer_container'),
30679
30680    isUse: function()
30681    {
30682        if (this.__$target !== null) {
30683            return true;
30684        }
30685        return false;
30686    },
30687
30688    execute: function()
30689    {
30690        EC$.ajax({
30691            url: '/order/async_basket_layer.html?__popupPage=T',
30692            async: false,
30693            success: function(data) {
30694                var sBasketLayerHtml = data;
30695                var sBasketLayerStyle = '';
30696                var sBasketLayerBody = '';
30697
30698                sBasketLayerHtml = sBasketLayerHtml.replace(/<script([\s\S]*?)<\/script>/gi,''); // 스크립트 제거
30699                sBasketLayerHtml = sBasketLayerHtml.replace(/<link([\s\S]*?)\/>/gi,''); // 옵티마이져 제거
30700
30701                var regexStyle = /<style([\s\S]*?)<\/style>/; // Style 추출
30702                if (regexStyle.exec(sBasketLayerHtml) != null) sBasketLayerStyle = regexStyle.exec(sBasketLayerHtml)[0];
30703
30704                var regexBody = /<body[\s\S]*?>([\s\S]*?)<\/body>/; // Body 추출
30705                var aBasketLayerBodyMatch = regexBody.exec(sBasketLayerHtml);
30706                if (aBasketLayerBodyMatch) {
30707                    // 구엔진(libxml2): fragment를 html/head/body로 감싸므로 body 내부만 사용
30708                    sBasketLayerBody = aBasketLayerBodyMatch[1];
30709                } else {
30710                    // 새엔진(xrlib): body 래핑 없이 style+fragment만 반환하므로 그대로 사용
30711                    // (style은 아래에서 합치므로 중복 방지 위해 별도 추출본은 비움)
30712                    sBasketLayerStyle = '';
30713                    sBasketLayerBody = sBasketLayerHtml;
30714                }
30715
30716                CAPP_ASYNC_METHODS.BasketLayer.__sBasketLayerHtml = sBasketLayerStyle + sBasketLayerBody;
30717            }
30718        });
30719        this.__$target.innerHTML = this.__sBasketLayerHtml;
30720    }
30721};
30722
30723/**
30724 * 비동기식 데이터 - Benefit
30725 */
30726CAPP_ASYNC_METHODS.aDatasetList.push('Grade');
30727CAPP_ASYNC_METHODS.Grade = {
30728    __aGrade: null,
30729    __$target: EC$('#sGradeAutoDisplayArea'),
30730
30731    isUse: function()
30732    {
30733        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
30734            if (this.__$target.length > 0) {
30735                return true;
30736            }
30737        }
30738
30739        return false;
30740    },
30741
30742    setData: function(aData)
30743    {
30744        this.__aGrade = aData;
30745    },
30746
30747    execute: function()
30748    {
30749        var __aData = this.__aGrade;
30750        var aFilter = ['bChangeMaxTypePrice', 'bChangeMaxTypePriceAndCount', 'bChangeMaxTypePriceOrCount', 'bChangeMaxTypeCount'];
30751
30752        var aMaxDisplayJson = {
30753            "bChangeMaxTypePrice": [
30754                {"sId": "sChangeMaxTypePriceArea"}
30755            ],
30756            "bChangeMaxTypePriceAndCount": [
30757                {"sId": "sChangeMaxTypePriceAndCountArea"}
30758            ],
30759            "bChangeMaxTypePriceOrCount": [
30760                {"sId": "sChangeMaxTypePriceOrCountArea"}
30761            ],
30762            "bChangeMaxTypeCount": [
30763                {"sId": "sChangeMaxTypeCountArea"}
30764            ]
30765        };
30766
30767        if (EC$('.sNextGroupIconArea').length > 0) {
30768            if (__aData['bDisplayNextGroupIcon'] === true) {
30769                EC$('.sNextGroupIconArea').removeClass('displaynone').show();
30770                EC$('.myshop_benefit_next_group_icon_tag').attr({alt: __aData['sNextGrade'], src: __aData['sNextGroupIcon']});
30771            } else {
30772                EC$('.sNextGroupIconArea').addClass('displaynone');
30773            }
30774        }
30775
30776        var sIsAutoGradeDisplay = "F";
30777        EC$.each(__aData, function(key, val) {
30778            if (EC$.inArray(key, aFilter) === -1) {
30779                return true;
30780            }
30781            if (val === true) {
30782                if (EC$('#'+aMaxDisplayJson[key][0].sId).length > 0) {
30783                    EC$('#' + aMaxDisplayJson[key][0].sId).removeClass('displaynone').show();
30784                }
30785                sIsAutoGradeDisplay = "T";
30786            }
30787        });
30788        if (sIsAutoGradeDisplay == "T" && EC$('#sGradeAutoDisplayArea .sAutoGradeDisplay').length > 0) {
30789            EC$('#sGradeAutoDisplayArea .sAutoGradeDisplay').addClass('displaynone');
30790        }
30791
30792        EC$.each(__aData, function(key, val) {
30793            if (EC$.inArray(key, aFilter) === -1) {
30794                if (EC$('.xans-member-var-' + key).length > 0) {
30795                    EC$('.xans-member-var-' + key).html(val);
30796                }
30797            }
30798        });
30799    }    
30800};
30801
30802/**
30803 * 비동기식 데이터 - Benefit
30804 */
30805CAPP_ASYNC_METHODS.aDatasetList.push('AutomaticGradeShow');
30806CAPP_ASYNC_METHODS.AutomaticGradeShow = {
30807    __aGrade: null,
30808    __$target: EC$('#sAutomaticGradeShowArea'),
30809
30810    isUse: function()
30811    {
30812        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
30813            if (this.__$target.length > 0) {
30814                return true;
30815            }
30816        }
30817        return false;
30818    },
30819
30820    setData: function(aData)
30821    {
30822        this.__aGrade = aData;
30823    },
30824
30825    execute: function()
30826    {
30827        var __aData = this.__aGrade;
30828
30829        /**
30830         * 아이콘 표기 제외
30831        if (EC$('.sNextGroupIconArea').length > 0) {
30832            if (__aData['bDisplayNextGroupIcon'] === true) {
30833                EC$('.sNextGroupIconArea').removeClass('displaynone').show();
30834                EC$('.myshop_benefit_next_group_icon_tag').attr({alt: __aData['sNextGrade'], src: __aData['sNextGroupIcon']});
30835            } else {
30836                EC$('.sNextGroupIconArea').addClass('displaynone');
30837            }
30838        }
30839         */
30840
30841        var sIsAutoGradeDisplay = "F";
30842        EC$.each(__aData, function(key, val) {
30843            if (val === true) {
30844                sIsAutoGradeDisplay = "T";
30845                return false;
30846            }
30847        });
30848        if (sIsAutoGradeDisplay == "T" && EC$('#sAutomaticGradeShowArea .sAutoGradeDisplay').length > 0) {
30849            EC$('#sAutomaticGradeShowArea .sAutoGradeDisplay').addClass('displaynone');
30850        }
30851
30852        EC$.each(__aData, function(key, val) {
30853            if (EC$('.xans-member-var-' + key).length > 0) {
30854                EC$('.xans-member-var-' + key).html(val);
30855            }
30856        });
30857    }    
30858};
30859
30860/**
30861 * 비동기식 데이터 - 비동기장바구니 레이어
30862 */
30863CAPP_ASYNC_METHODS.aDatasetList.push('MobileMutiPopup');
30864CAPP_ASYNC_METHODS.MobileMutiPopup = {
30865    __$target: EC$('div[class^="ec-async-multi-popup-layer-container"]'),
30866
30867    isUse: function()
30868    {
30869        if (this.__$target.length > 0) {
30870            return true;
30871        }
30872        return false;
30873    },
30874
30875    execute: function()
30876    {
30877        for (var i=0; i < this.__$target.length; i++) {
30878            EC$.ajax({
30879                url: '/exec/front/popup/AjaxMultiPopup?index='+i,
30880                data: EC_ASYNC_MULTI_POPUP_OPTION[i],
30881                dataType: "json",
30882                success: function (oResult) {
30883                    switch (oResult.code) {
30884                        case '0000' :
30885                            if (oResult.data.length < 1) {
30886                                break;
30887                            }
30888                            EC$('.ec-async-multi-popup-layer-container-' + oResult.data.html_index).html(oResult.data.html_text);
30889                            if (oResult.data.type == 'P') {
30890                                BANNER_POPUP_OPEN.setPopupSetting();
30891                                BANNER_POPUP_OPEN.setPopupWidth();
30892                                BANNER_POPUP_OPEN.setPopupClose();
30893                            } else {
30894                                /**
30895                                 * 이중 스크롤 방지 클래스 추가(비동기) 
30896                                 *
30897                                 */
30898                                EC$('body').addClass('eMobilePopup');
30899                                EC$('body').width('100%');
30900
30901                                BANNER_POPUP_OPEN.setFullPopupSetting();
30902                                BANNER_POPUP_OPEN.setFullPopupClose();
30903                            }
30904                            break;
30905                        default :
30906                            break;
30907                    }
30908                },
30909                error: function () {
30910                }
30911            });
30912        }
30913    }
30914};
30915
30916/**
30917 * sns 연동 정보
30918 */
30919CAPP_ASYNC_METHODS.aDatasetList.push('CustomerProvider');
30920CAPP_ASYNC_METHODS.CustomerProvider = {
30921    STORAGE_KEY: 'CustomerProvider_' + CAFE24.SDE_SHOP_NUM,
30922    aData: {member_id: null, provider: null},
30923
30924    isUse: function()
30925    {
30926        return false;
30927    },
30928
30929    restoreCache: function() {
30930        // sessionStorage 지원 여부 확인
30931        if (!window.sessionStorage) {
30932            return false;
30933        }
30934
30935        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
30936        if (sSessionStorageData === null) {
30937            return false;
30938        }
30939
30940        try {
30941
30942            var jsonData = JSON.parse(this.aData);
30943            var aStorageData = JSON.parse(sSessionStorageData);
30944            
30945            // 캐쉬 데이터 설정
30946            jsonData.member_id = aStorageData.membr_id;
30947            jsonData.provider = aStorageData.provider;
30948
30949            this.aData = jsonData;
30950
30951            return true;
30952        } catch (e) {
30953
30954            // 복구 실패시 캐시 삭제
30955            this.removeCache();
30956
30957            return false;
30958        }
30959    },
30960
30961    removeCache: function()
30962    {
30963        // sessionStorage 지원 여부 확인
30964        if (!window.sessionStorage) {
30965            return;
30966        }
30967        // 캐시 삭제
30968        window.sessionStorage.removeItem(this.STORAGE_KEY);
30969    },
30970
30971    setData: function(aData)
30972    {
30973        this.aData = JSON.stringify(aData);
30974        // sessionStorage 지원 여부 확인
30975        if (!window.sessionStorage) {
30976            return false;
30977        }
30978
30979        try {
30980            sessionStorage.setItem(this.STORAGE_KEY, this.aData);
30981        } catch (error) {
30982        }
30983    },
30984
30985    execute: function()
30986    {
30987
30988    },
30989
30990    getData: function()
30991    {
30992        if (typeof this.aData.member_id === 'object') return this.aData;
30993        return JSON.parse(this.aData);
30994    }
30995};
30996
30997var eCacheDefaultShippingAddress = null;
30998if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
30999    CAFE24.CRYPTOKEY.decryptAndLoad('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM).then(function(o) {
31000        eCacheDefaultShippingAddress = o;
31001    }).catch(function(err) {
31002        // console.log(err);
31003    });
31004}
31005
31006
31007/**
31008 * 비동기식 데이터 - 기본 배송주소
31009 */
31010CAPP_ASYNC_METHODS.aDatasetList.push('DefaultShippingAddress');
31011CAPP_ASYNC_METHODS.DefaultShippingAddress = {
31012
31013    __sShippingAlias: null,
31014    __sZipCode: null,
31015    __sAddress1: null,
31016    __sAddress2: null,
31017    __bRestoredFromCache: false,
31018
31019    isUse: function()
31020    {
31021        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
31022            // ECHOSTING-635081: 조회 결과의 유일한 소비처는 App Script SDK
31023            // (CAFE24API.getDefaultShippingAddress -> AppCommon -> getData) 이며,
31024            // 그 진입점이 'customer' scope 를 이미 검사한다. 로그인 UI 존재 여부는
31025            // 배송지 소비와 무관한데도 전 페이지뷰 선조회를 유발했으므로 판정에서 제외한다.
31026            if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('customer', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
31027                return true;
31028            }
31029
31030        } else {
31031            // 비 로그인 상태에서 삭제처리
31032            this.removeCache();
31033        }
31034
31035        return false;
31036    },
31037
31038    restoreCache: function()
31039    {
31040        // sessionStorage 지원 여부 확인
31041        if (!window.sessionStorage) {
31042            return false;
31043        }
31044
31045        // 데이터 복구 유무
31046        var bRestored = false;
31047
31048        try {
31049            // 데이터 복구
31050            var oCache = null;
31051            if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
31052                if (eCacheDefaultShippingAddress) {
31053                    oCache = JSON.parse(eCacheDefaultShippingAddress);
31054                }
31055            } else if (window.location.protocol === 'http:' && (typeof CryptoJS) == 'object') {
31056                if (window.sessionStorage.getItem('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM)) {
31057                    oCache = window.sessionStorage.getItem('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31058                    oCache = CryptoJS.AES.decrypt(oCache, SHOP.getMallID() + '_' + EC_SDE_SHOP_NUM).toString(CryptoJS.enc.Utf8);
31059                    oCache = JSON.parse(oCache);
31060                }
31061            }
31062
31063            // expire 체크
31064            if (oCache.exp < Date.now()) {
31065                throw new Error('cache has expired.');
31066            }
31067
31068            // 데이터 체크
31069            if (typeof oCache.data === 'undefined'
31070                || oCache.data === null
31071                || typeof oCache.data.shipping_alias === 'undefined'
31072                || typeof oCache.data.zip_code === 'undefined'
31073                || typeof oCache.data.address1 === 'undefined'
31074                || typeof oCache.data.address2 === 'undefined'
31075            ) {
31076                throw new Error('Invalid cache data.');
31077            }
31078
31079            // 데이터 복구
31080            this.__sShippingAlias = oCache.data.shipping_alias;
31081            this.__sZipCode = oCache.data.zip_code;
31082            this.__sAddress1 = oCache.data.address1;
31083            this.__sAddress2 = oCache.data.address2;
31084
31085            this.__bRestoredFromCache = true;
31086            bRestored = true;
31087        } catch (e) {
31088            // 복구 실패시 캐시 삭제
31089            this.removeCache();
31090        }
31091
31092        return bRestored;
31093    },
31094
31095    cache: function()
31096    {
31097        // sessionStorage 지원 여부 확인
31098        if (!window.sessionStorage) {
31099            return;
31100        }
31101        // 캐시
31102        var sData = JSON.stringify({exp: Date.now() + (1000 * 60 * 10), data: this.getData()});
31103
31104        if (window.location.protocol === 'https:' && (typeof CAFE24.CRYPTOKEY) == 'object') {
31105            CAFE24.CRYPTOKEY.encryptAndSave('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM, sData);
31106        } else if (window.location.protocol === 'http:' && (typeof CryptoJS) == 'object') {
31107            sData = CryptoJS.AES.encrypt(sData, SHOP.getMallID() + '_' + EC_SDE_SHOP_NUM).toString();
31108            window.sessionStorage.setItem('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM, sData);
31109        }
31110    },
31111
31112    removeCache: function()
31113    {
31114        // sessionStorage 지원 여부 확인
31115        if (!window.sessionStorage) {
31116            return;
31117        }
31118
31119        // 캐시 삭제
31120        window.sessionStorage.removeItem('encryptedData_DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31121        window.sessionStorage.removeItem('iv_DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31122        window.sessionStorage.removeItem('eto4DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31123        window.sessionStorage.removeItem('eto5DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31124        window.sessionStorage.removeItem('DefaultShippingAddress_' + CAFE24.SDE_SHOP_NUM);
31125    },
31126
31127    setData: function(oData)
31128    {
31129        if (oData === null || typeof oData === 'undefined') {
31130            return;
31131        }
31132
31133        var val = function(v) {
31134            return (v === null || typeof v === 'undefined') ? null : v;
31135        };
31136
31137        this.__sShippingAlias = val(oData.shipping_alias);
31138        this.__sZipCode = val(oData.zip_code);
31139        this.__sAddress1 = val(oData.address1);
31140        this.__sAddress2 = val(oData.address2);
31141    },
31142
31143    execute: function()
31144    {
31145        // ECHOSTING-635081: 캐시에서 복원된 경우 재저장하지 않는다.
31146        // async.js setData() 는 캐시 히트 모듈에도 execute() 를 호출하므로, 그대로 두면
31147        // 매 페이지뷰마다 exp 가 갱신되어 TTL 이 슬라이딩 윈도우가 된다(= 활성 세션 동안 만료 안 됨).
31148        // 어드민/API 로 배송지가 바뀌었을 때의 노출 창을 10분으로 고정하기 위해 분기한다.
31149        if (this.__bRestoredFromCache === true) {
31150            return;
31151        }
31152        this.cache();
31153    },
31154
31155    getData: function()
31156    {
31157        if (CAPP_ASYNC_METHODS.STATUS == 'unready') {
31158            CAPP_ASYNC_METHODS.init();
31159        }
31160
31161        if (this.__sShippingAlias === null
31162            && this.__sZipCode === null
31163            && this.__sAddress1 === null
31164            && this.__sAddress2 === null) {
31165            return null;
31166        }
31167
31168        return {
31169            shipping_alias: this.__sShippingAlias,
31170            zip_code: this.__sZipCode,
31171            address1: this.__sAddress1,
31172            address2: this.__sAddress2
31173        };
31174    }
31175};
31176
31177/**
31178 * 비동기식 데이터 - 좋아요 상품 갯수
31179 */
31180CAPP_ASYNC_METHODS.aDatasetList.push('MyLikeProductCount');
31181CAPP_ASYNC_METHODS.MyLikeProductCount = {
31182    __iMyLikeCount: null,
31183
31184    __$target: EC$('#xans_myshop_like_prd_cnt'),
31185    __$target_main: EC$('#xans_myshop_main_like_prd_cnt'),
31186    isUse: function()
31187    {
31188        if (this.__$target.length > 0 && SHOP.getLanguage() === 'ko_KR') {
31189            return true;
31190        }
31191
31192        if (this.__$target_main.length > 0 && SHOP.getLanguage() === 'ko_KR') {
31193            return true;
31194        }
31195
31196        return false;
31197    },
31198    restoreCache: function()
31199    {
31200        var sCookieName = 'like_product_cnt' + CAFE24.SDE_SHOP_NUM;
31201        var re = new RegExp('(?:^| |;)' + sCookieName + '=([^;]+)');
31202        var aCookieValue = document.cookie.match(re);
31203        if (aCookieValue) {
31204            this.__iMyLikeCount = parseInt(aCookieValue[1], 10);
31205            return true;
31206        }
31207
31208        return false;
31209    },
31210
31211    setData: function(sData)
31212    {
31213        this.__iMyLikeCount = Number(sData);
31214    },
31215
31216    execute: function()
31217    {
31218        if (SHOP.getLanguage() === 'ko_KR') {
31219            this.__$target.html(this.__iMyLikeCount + '개');
31220            this.__$target_main.html(this.__iMyLikeCount);
31221        }
31222    }
31223};
31224
31225/**
31226 * 비동기식 데이터 - 좋아요 상품 list
31227 */
31228CAPP_ASYNC_METHODS.aDatasetList.push('MyLikeProductList');
31229CAPP_ASYNC_METHODS.MyLikeProductList = {
31230    __aMyLikeList: null,
31231    __iMyLikeListLimit: 10,
31232    __$target: EC$('.xans-product-likeproductasync'),
31233    isUse: function()
31234    {
31235        if (this.__$target.length > 0 && SHOP.getLanguage() === 'ko_KR') {
31236            return true;
31237        }
31238
31239        if (EC$('#EC_LIKE_ASYNC_LINK_DATA_LIST').length > 0) {
31240            return true;
31241        }
31242        return false;
31243    },
31244    setData: function(aData)
31245    {
31246        this.__iMyLikeListLimit = EC_FRONT_JS_CONFIG_SHOP.aSyncLikeLimit;
31247        this.__aMyLikeList = aData;
31248    },
31249    execute: function()
31250    {
31251
31252        if (this.__aMyLikeList === null || this.__aMyLikeList.length === 0) {
31253            EC$('#EC_LIKE_ASYNC_LINK_DATA_EMPTY').html('');
31254            return;
31255        }
31256
31257        //EC$('#EC_LIKE_ASYNC_LINK_DATA_EMPTY').remove();
31258        var sSpaceIcon = ' ';
31259        for (var iKey = 0; iKey < this.__aMyLikeList.length; iKey++) {
31260            var oRowData = EC$('#EC_LIKE_ASYNC_LINK_DATA_LIST_TEMP').clone().removeAttr('id');
31261            oRowData.find('a[href^="/product/detail.html"').attr('href', this.__aMyLikeList[iKey].link_product_detail);
31262            oRowData.find('.thumb img').attr('src',this.__aMyLikeList[iKey].image_medium);
31263            oRowData.find('.EC_LIKE_ASYNC_LINK_DATA_PRODUCT_NAME').html('<a href="' + this.__aMyLikeList[iKey].link_product_detail + '">' + this.__aMyLikeList[iKey].disp_product_name + '</a>');
31264
31265            var sIconListHtml = this.__aMyLikeList[iKey].soldout_icon + sSpaceIcon + this.__aMyLikeList[iKey].stock_icon + sSpaceIcon + this.__aMyLikeList[iKey].recommend_icon + sSpaceIcon +
31266                this.__aMyLikeList[iKey].new_icon + sSpaceIcon + this.__aMyLikeList[iKey].product_icons + sSpaceIcon + this.__aMyLikeList[iKey].benef
31266it_icons;
31267             if (sIconListHtml !== '') {
31268                oRowData.find('.EC_LIKE_ASYNC_LINK_DATA_ICON_LIST').html(sIconListHtml);
31269            }
31270
31271            EC$('#EC_LIKE_ASYNC_LINK_DATA_APPEND').append(oRowData);
31272
31273            if (iKey >= (this.__iMyLikeListLimit - 1)) {
31274                break;
31275            }
31276        }
31277        EC$('#EC_LIKE_ASYNC_LINK_DATA_LIST_TEMP').remove();
31278        if (this.__aMyLikeList.length < this.__iMyLikeListLimit) {
31279            EC$('#EC_LIKE_ASYNC_LINK_DATA_MORE_VIEW').remove();
31280        }
31281
31282        if (EC_FRONT_JS_CONFIG_SHOP.bAutoView === 'T') {
31283            document.getElementById('EC_LIKE_ASYNC_LINK_DATA_LIST').style.display = 'block';
31284        }
31285
31286    }
31287};
31288
31289/**
31290 * 비동기식 데이터 - 마이쇼핑 > 주문 카운트 (주문 건수 / CS건수 / 예전주문)
31291 */
31292CAPP_ASYNC_METHODS.aDatasetList.push('OrderHistoryCount');
31293CAPP_ASYNC_METHODS.OrderHistoryCount = {
31294    __sTotalOrder: null,
31295    __sTotalOrderCs: null,
31296    __sTotalOrderOld: null,
31297
31298    __$target: EC$('#ec_myshop_total_orders'),
31299    __$target2: EC$('#ec_myshop_total_orders_cs'),
31300    __$target3: EC$('#ec_myshop_total_orders_old'),
31301
31302    isUse: function()
31303    {
31304        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
31305            if (this.__$target.length > 0) {
31306                return true;
31307            }
31308
31309            if (this.__$target2.length > 0) {
31310                return true;
31311            }
31312
31313            if (this.__$target3.length > 0) {
31314                return true;
31315            }
31316        }
31317
31318        return false;
31319    },
31320
31321    setData: function(aData)
31322    {
31323        this.__sTotalOrder = aData['total_orders'];
31324        this.__sTotalOrderCs = aData['total_orders_cs'];
31325        this.__sTotalOrderOld = aData['total_orders_old'];
31326
31327    },
31328
31329    execute: function()
31330    {
31331        this.__$target.html(this.__sTotalOrder);
31332        this.__$target2.html(this.__sTotalOrderCs);
31333        this.__$target3.html(this.__sTotalOrderOld);
31334    }
31335};
31336
31337/**
31338 * 주문조회 > 주문내역조회 및 취소/교환/반품내역 등 탭(OrderHistoryTab) 갯수 비동기호출
31339 */
31340CAPP_ASYNC_METHODS.aDatasetList.push('OrderHistoryTab');
31341CAPP_ASYNC_METHODS.OrderHistoryTab = {
31342    __$targetTotalOrders: EC$('#xans_myshop_total_orders'),
31343    __$targetTotalOrdersCs: EC$('#xans_myshop_total_orders_cs'),
31344    __$targetTotalOrdersPast: EC$('#xans_myshop_total_orders_past'),
31345    __$targetTotalOrdersOld: EC$('#xans_myshop_total_orders_old'),
31346
31347    isUse: function()
31348    {
31349        if (EC$('.xans-myshop-orderhistorytab').length > 0) {
31350            return true;
31351        }
31352        return false;
31353    },
31354    execute: function()
31355    {
31356        try {
31357            var mode = this.getUrlParam('mode');
31358            var order_id = this.getUrlParam('order_id');
31359            var order_status = this.getUrlParam('order_status');
31360            var history_start_date = this.getUrlParam('history_start_date');
31361            var history_end_date = this.getUrlParam('history_end_date');
31362            var past_year = this.getUrlParam('past_year');
31363            var count = this.getUrlParam('count');
31364
31365            var sPathName = window.location.pathname;
31366
31367            var oParameters = {
31368                'mode': mode == null ? '' : mode,
31369                'order_id': order_id == null ? '' : order_id,
31370                'order_status': order_status == null ? '' : order_status,
31371                'history_start_date': history_start_date == null ? '' : history_start_date,
31372                'history_end_date': history_end_date == null ? '' : history_end_date,
31373                'past_year': past_year == null ? '' : past_year,
31374                'count': count == null ? '' : count,
31375                'page_name': sPathName.substring(sPathName.lastIndexOf("/") + 1, sPathName.indexOf('.'))
31376            };
31377
31378            if (typeof EC_ASYNC_ORDERHISTORYTAB_ORDER_ID !== 'undefined') {
31379                oParameters['encrypted_str'] = EC_ASYNC_ORDERHISTORYTAB_ORDER_ID;
31380            }
31381
31382            var oThis = this;
31383
31384            EC$.ajax({
31385                url: '/exec/front/Myshop/OrderHistoryTab',
31386                dataType: 'json',
31387                data: oParameters,
31388                success: function (aData) {
31389                    if (aData['result'] === true) {
31390                        oThis.__$targetTotalOrders.html(aData['total_orders']);
31391                        oThis.__$targetTotalOrdersCs.html(aData['total_orders_cs']);
31392                        oThis.__$targetTotalOrdersOld.html(aData['total_orders_old']);
31393                        oThis.__$targetTotalOrdersPast.html(aData['total_orders_past']);
31394
31395                        var oTabATagList = {
31396                            'param': EC$('.tab_class a'),
31397                            'param_cs': EC$('.tab_class_cs a'),
31398                            'param_past': EC$('.tab_class_past a'),
31399                            'param_old': EC$('.tab_class_old a'),
31400                        };
31401                        var sHref;
31402                        EC$.each(oTabATagList, function(sKey, oTarget) {
31403                            if (oTarget.length > 0) {
31404                                sHref = oTarget.attr("href");
31405                                sHref = sHref.replace("$" + sKey, aData[sKey]);
31406                                oTarget.attr("href", sHref);
31407                            }
31408                        });
31409
31410                        EC$("." + aData['selected_tab_class']).addClass('selected');
31411
31412                        if (aData['is_past_list_display'] === false) {
31413                            EC$('.tab_class_past').addClass("displaynone");
31414                        } else {
31415                            EC$('.tab_class_past').removeClass("displaynone");
31416                        }
31417
31418                        if (aData['old_list_display'] === false) {
31419                            EC$('.tab_class_old').addClass("displaynone");
31420                        } else {
31421                            EC$('.tab_class_old').removeClass("displaynone");
31422                        }
31423                    }
31424                }
31425            });
31426        } catch (oError) {
31427            this.errorAjaxCall(oError);
31428        }
31429    },
31430    getUrlParam: function(name)
31431    {
31432        var param = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
31433        if (param == null) {
31434            return null;
31435        } else {
31436            return decodeURI(param[1]) || null;
31437        }
31438    },
31439    errorAjaxCall: function(oError)
31440    {
31441        var sError = oError.toString();
31442        var aMatch = sError.match(/Error*/g);
31443
31444        if (typeof(oError) !== 'object' || aMatch == null || aMatch.length < 1 || !oError.stack) return;
31445
31446        EC$.ajax({
31447            url: '/exec/front/order/FormJserror/',
31448            method: 'POST',
31449            cache: false,
31450            async: false,
31451            data: {
31452                errorMessage: oError.message,
31453                errorStack: oError.stack,
31454                errorName: oError.name
31455            }
31456        });
31457    }
31458};
31459
31460/*
31461 * 비동기식 데이터 - 주문조회 품목 리스트
31462 */
31463CAPP_ASYNC_METHODS.aDatasetList.push('OrderHistoryItemList');
31464CAPP_ASYNC_METHODS.OrderHistoryItemList = {
31465
31466    STORAGE_KEY: 'OrderHistoryItemList_' + EC_SDE_SHOP_NUM,
31467
31468    __aData: null,
31469
31470    isUse: function()
31471    {
31472        // 주문조회 페이지 && SDK order권한이 있는경우에만 노출
31473        if (EC$('.xans-myshop-orderhistorylistitem').length > 0 && typeof EC_APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('order', EC_APPSCRIPT_SDK_DATA) > -1) {
31474            return true;
31475        }
31476
31477        // 비 로그인 상태에서 삭제처리
31478        if (CAPP_ASYNC_METHODS.IS_LOGIN === false) {
31479            this.removeCache();
31480        }
31481    },
31482
31483    restoreCache: function()
31484    {
31485        // sessionStorage 지원 여부 확인
31486        if (!window.sessionStorage) {
31487            return false;
31488        }
31489
31490        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
31491        if (sSessionStorageData === null || !sSessionStorageData) {
31492            return false;
31493        }
31494
31495        try {
31496            var aStorageData = JSON.parse(sSessionStorageData);
31497
31498            // expire 체크
31499            if (aStorageData.exp < Date.now()) {
31500                throw 'cache has expired.';
31501            }
31502
31503            this.__aData = [];
31504            for (var iKey in aStorageData.data) {
31505                this.__aData.push(aStorageData.data[iKey]);
31506            }
31507
31508            return true;
31509        } catch (e) {
31510
31511            // 복구 실패시 캐시 삭제
31512            this.removeCache();
31513
31514            return false;
31515        }
31516    },
31517
31518    removeCache: function()
31519    {
31520        // sessionStorage 지원 여부 확인
31521        if (!window.sessionStorage) {
31522            return;
31523        }
31524        // 캐시 삭제
31525        window.sessionStorage.removeItem(this.STORAGE_KEY);
31526    },
31527
31528    setData: function(oData)
31529    {
31530        this.__aData = oData;
31531
31532        // sessionStorage 지원 여부 확인
31533        if (!window.sessionStorage) {
31534            return;
31535        }
31536
31537        try {
31538            sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify({
31539                exp: Date.now() + (1000 * 60 * 1),
31540                data: this.getData()
31541            }));
31542        } catch (error) {
31543        }
31544    },
31545
31546    getData: function()
31547    {
31548        this.restoreCache();
31549        var aStorageData = this.__aData;
31550
31551        if (aStorageData !== null && aStorageData !== false) {
31552            var oNewStorageData = [];
31553            for (var iKey in aStorageData) {
31554                oNewStorageData.push(aStorageData[iKey]);
31555            }
31556            return oNewStorageData;
31557        }
31558        return false;
31559    },
31560
31561    getAsyncData: function(start_date, end_date, order_status, page, count, order_id)
31562    {
31563        return new Promise(function (res) {
31564            var oParameters = {
31565                'order_id': order_id === null ? '' : order_id,
31566                'order_status': order_status === null ? '' : order_status,
31567                'start_date': start_date === null ? '' : start_date,
31568                'end_date': end_date === null ? '' : end_date,
31569                'count': count === null ? '' : count,
31570                'page': page === null ? '' : page,
31571            };
31572
31573            if (typeof EC_ASYNC_ORDERHISTORYTAB_ORDER_ID !== 'undefined') {
31574                oParameters['encrypted_str'] = EC_ASYNC_ORDERHISTORYTAB_ORDER_ID;
31575            }
31576
31577            EC$.ajax(
31578                {
31579                    url: '/exec/front/manage/async?module=OrderHistoryItemList',
31580                    data: oParameters,
31581                    dataType: 'json',
31582                    success: function (aData) {
31583                        var aResult = [];
31584                        var aStorageData = aData.OrderHistoryItemList;
31585
31586                        for (var iKey in aStorageData) {
31587                            aResult.push(aStorageData[iKey]);
31588                        }
31589                        res(aResult);
31590                    }
31591                });
31592        });
31593    }
31594};
31595/**
31596 * 비동기식 데이터 - 주문상세 조회
31597 */
31598CAPP_ASYNC_METHODS.aDatasetList.push('OrderDetailInfo');
31599CAPP_ASYNC_METHODS.OrderDetailInfo = {
31600    STORAGE_KEY: 'OrderDetailInfo_' + EC_SDE_SHOP_NUM,
31601    __aData: null,
31602
31603
31604    isUse: function()
31605    {
31606        // 주문상세 페이지 && SDK order권한이 있는경우에만 노출
31607        if (EC$('.xans-myshop-orderhistorydetail').length > 0 && typeof EC_APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('order', EC_APPSCRIPT_SDK_DATA) > -1) {
31608            return true;
31609        }
31610
31611        // 비 로그인 상태에서 삭제처리
31612        if (CAPP_ASYNC_METHODS.IS_LOGIN === false) {
31613            this.removeCache();
31614        }
31615    },
31616
31617    restoreCache: function()
31618    {
31619        // sessionStorage 지원 여부 확인
31620        if (!window.sessionStorage) {
31621            return false;
31622        }
31623
31624        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
31625        if (sSessionStorageData === null || !sSessionStorageData) {
31626            return false;
31627        }
31628
31629        try {
31630            var aStorageData = JSON.parse(sSessionStorageData);
31631
31632            // expire 체크
31633            if (aStorageData.exp < Date.now()) {
31634                throw 'cache has expired.';
31635            }
31636
31637            this.__aData = [];
31638            for (var iKey in aStorageData.data) {
31639                this.__aData.push(aStorageData.data[iKey]);
31640            }
31641
31642            return true;
31643        } catch (e) {
31644
31645            // 복구 실패시 캐시 삭제
31646            this.removeCache();
31647
31648            return false;
31649        }
31650    },
31651
31652
31653    removeCache: function()
31654    {
31655        // sessionStorage 지원 여부 확인
31656        if (!window.sessionStorage) {
31657            return;
31658        }
31659        // 캐시 삭제
31660        window.sessionStorage.removeItem(this.STORAGE_KEY);
31661    },
31662
31663    setData: function(oData)
31664    {
31665        this.__aData = oData;
31666
31667        // sessionStorage 지원 여부 확인
31668        if (!window.sessionStorage) {
31669            return;
31670        }
31671
31672        try {
31673            sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify({
31674                exp: Date.now() + (1000 * 60 * 1),
31675                data: this.getData()
31676            }));
31677        } catch (error) {
31678        }
31679    },
31680
31681    getData: function()
31682    {
31683        this.restoreCache();
31684        var aStorageData = this.__aData;
31685
31686        if (aStorageData !== null && aStorageData !== false) {
31687            var oNewStorageData = [];
31688            for (var iKey in aStorageData) {
31689                oNewStorageData.push(aStorageData[iKey]);
31690            }
31691            return oNewStorageData;
31692        }
31693        return false;
31694    },
31695
31696    getAsyncData: function (shop_no, order_id) {
31697        return new Promise(function (res) {
31698            var oParameters = {
31699                'shop_no': shop_no === null ? '' : shop_no,
31700                'order_id': order_id === null ? '' : order_id,
31701            };
31702
31703            if (typeof EC_ASYNC_ORDERHISTORYTAB_ORDER_ID !== 'undefined') {
31704                oParameters['encrypted_str'] = EC_ASYNC_ORDERHISTORYTAB_ORDER_ID;
31705            }
31706
31707            EC$.ajax(
31708                {
31709                    url: '/exec/front/manage/async?module=OrderDetailInfo',
31710                    data: oParameters,
31711                    dataType: 'json',
31712                    success: function (aData) {
31713                        var aResult = [];
31714                        var aStorageData = aData.OrderDetailInfo;
31715
31716                        for (var iKey in aStorageData) {
31717                            aResult.push(aStorageData[iKey]);
31718                        }
31719                        res(aResult);
31720                    }
31721                });
31722        });
31723    }
31724}
31725/**
31726 * 비동기식 데이터 - 주문 취소/교환/반품 가능 품목리스트
31727 */
31728CAPP_ASYNC_METHODS.aDatasetList.push('ClaimableItemList');
31729CAPP_ASYNC_METHODS.ClaimableItemList = {
31730    STORAGE_KEY: 'ClaimableItemList_' + EC_SDE_SHOP_NUM,
31731    __aData: null,
31732    __$target: EC$('.xans-myshop-orderhistoryapplycancel'), //취소
31733    __$target2: EC$('.xans-myshop-orderhistoryapplyexchange'), //교환
31734    __$target3: EC$('.xans-myshop-orderhistoryapplyreturn'), //반품
31735
31736    isUse: function()
31737    {
31738        // 주문상세 페이지 && SDK order권한이 있는경우에만 노출
31739        if (typeof EC_APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('order', EC_APPSCRIPT_SDK_DATA) > -1) {
31740            if (this.__$target.length > 0) {
31741                return true;
31742            }
31743            if (this.__$target2.length > 0) {
31744                return true;
31745            }
31746            if (this.__$target3.length > 0) {
31747                return true;
31748            }
31749        }
31750
31751        // 비 로그인 상태에서 삭제처리
31752        if (CAPP_ASYNC_METHODS.IS_LOGIN === false) {
31753            this.removeCache();
31754        }
31755    },
31756
31757    restoreCache: function()
31758    {
31759        // sessionStorage 지원 여부 확인
31760        if (!window.sessionStorage) {
31761            return false;
31762        }
31763
31764        var sSessionStorageData = window.sessionStorage.getItem(this.STORAGE_KEY);
31765        if (sSessionStorageData === null || !sSessionStorageData) {
31766            return false;
31767        }
31768
31769        try {
31770            var aStorageData = JSON.parse(sSessionStorageData);
31771
31772            // expire 체크
31773            if (aStorageData.exp < Date.now()) {
31774                throw new Error('cache has expired.');
31775            }
31776
31777            this.__aData = [];
31778            for (var iKey in aStorageData.data) {
31779                this.__aData.push(aStorageData.data[iKey]);
31780            }
31781
31782            return true;
31783        } catch (e) {
31784            // 복구 실패시 캐시 삭제
31785            this.removeCache();
31786            return false;
31787        }
31788    },
31789
31790
31791    removeCache: function()
31792    {
31793        // sessionStorage 지원 여부 확인
31794        if (!window.sessionStorage) {
31795            return;
31796        }
31797        // 캐시 삭제
31798        window.sessionStorage.removeItem(this.STORAGE_KEY);
31799    },
31800
31801    setData: function(oData)
31802    {
31803        this.__aData = oData;
31804
31805        // sessionStorage 지원 여부 확인
31806        if (!window.sessionStorage) {
31807            return;
31808        }
31809
31810        try {
31811            sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify({
31812                exp: Date.now() + (1000 * 60 * 1),
31813                data: this.getData()
31814            }));
31815        } catch (error) {
31816        }
31817    },
31818
31819    getData: function()
31820    {
31821        this.restoreCache();
31822        var aStorageData = this.__aData;
31823
31824        if (aStorageData !== null && aStorageData !== false && aStorageData.length > 0) {
31825            var oNewStorageData = [];
31826            for (var iKey in aStorageData) {
31827                oNewStorageData.push(aStorageData[iKey]);
31828            }
31829            return oNewStorageData;
31830        }
31831        return false;
31832    },
31833
31834    getAsyncData: function (order_id, customer_service_type) {
31835        return new Promise(function (res,rej) {
31836            if (customer_service_type === null) {
31837                if (CAPP_ASYNC_METHODS.ClaimableItemList.__$target.length > 0) {
31838                    customer_service_type = 'C';
31839                } else if (CAPP_ASYNC_METHODS.ClaimableItemList.__$target2.length > 0) {
31840                    customer_service_type = 'E';
31841                } else if (CAPP_ASYNC_METHODS.ClaimableItemList.__$target3.length > 0) {
31842                    customer_service_type = 'R';
31843                }
31844            }
31845            order_id = order_id === null ? CAPP_ASYNC_METHODS.ClaimableItemList.getUrlParam('order_id') : order_id;
31846
31847            if (order_id === '' || order_id === null) {
31848                rej({code: 422, message: 'order_id is empty'})
31849            }
31850
31851            var oParameters = {
31852                'order_id': order_id,
31853                'customer_service_type': customer_service_type
31854            };
31855
31856            EC$.ajax(
31857                {
31858                    url: '/exec/front/manage/async?module=ClaimableItemList',
31859                    data: oParameters,
31860                    dataType: 'json',
31861                    success: function (aData) {
31862                        var aResult = [];
31863                        var aStorageData = aData.ClaimableItemList;
31864
31865                        CAPP_ASYNC_METHODS.ClaimableItemList.removeCache();
31866                        CAPP_ASYNC_METHODS.ClaimableItemList.setData(aStorageData);
31867
31868                        for (var iKey in aStorageData) {
31869                            aResult.push(aStorageData[iKey]);
31870                        }
31871                        res(aResult);
31872                    }
31873                });
31874        });
31875    },
31876    getUrlParam: function(name)
31877    {
31878        var param = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
31879        if (param === null) {
31880            return null;
31881        } else {
31882            return decodeURI(param[1]) || null;
31883        }
31884    }
31885};
31886var PathRoleValidator = (function() {
31887    /**
31888     * Milage, Deposit 의 경우 처리되지 말아야할 페이지 확인
31889     * @returns {boolean}
31890     */
31891    function isInvalidPathRole()
31892    {
31893        // path role
31894        var sCurrentPathRole = null;
31895
31896        // // euckr 환경에서 path role 획득
31897        if (SHOP.getProductVer() === 1) {
31898            // path 와 role 매핑
31899            var aPathRoleMap = {
31900                '/myshop/index.html': 'MYSHOP_MAIN',
31901                '/myshop/mileage/historyList.html': 'MYSHOP_MILEAGE_LIST',
31902                '/myshop/deposits/historyList.html': 'MYSHOP_DEPOSIT_LIST',
31903                '/order/orderform.html': 'ORDER_ORDERFORM'
31904            };
31905
31906            // 페이지 경로로부터 path role 획득
31907            sCurrentPathRole = aPathRoleMap[document.location.pathname];
31908
31909            // utf8 환경에서 path role 획득
31910        } else {
31911            // 현재 페이지 path role 획득
31912            sCurrentPathRole = EC$('meta[name="path_role"]').attr('content');
31913        }
31914
31915        // 처리되면 안되는 경로
31916        var aInvalidPathRole = [
31917            'MYSHOP_MAIN',
31918            'MYSHOP_MILEAGE_LIST',
31919            'MYSHOP_DEPOSIT_LIST',
31920            'ORDER_ORDERFORM'
31921        ];
31922
31923        return EC$.inArray(sCurrentPathRole, aInvalidPathRole) >= 0;
31924    }
31925
31926    return {
31927        isInvalidPathRole: isInvalidPathRole
31928    };
31929})();
31930
31931EC$(function()
31932{
31933    CAPP_ASYNC_METHODS.init();
31934});
31935
31936CAFE24.MANAGE_PRODUCT_RECENT = {
31937    getRecentImageUrl: function() {
31938        var sStorageKey = 'localRecentProduct' + CAFE24.SDE_SHOP_NUM;
31939
31940        if (typeof sessionStorage[sStorageKey] !== 'undefined') {
31941            var sRecentData = sessionStorage.getItem(sStorageKey);
31942            var oJsonData = JSON.parse(sRecentData);
31943            var sImageSrc = '';
31944
31945            if (oJsonData[0] !== undefined) {
31946                sImageSrc = oJsonData[0].sImgSrc;
31947                if (typeof EC_FRONT_JS_CONFIG_MANAGE !== 'undefined' && typeof EC_FRONT_JS_CONFIG_MANAGE.cdnUrl !== 'undefined' && EC_FRONT_JS_CONFIG_MANAGE.cdnUrl !== '') {
31948                    sImageSrc = EC_FRONT_JS_CONFIG_MANAGE.cdnUrl + '/web/product/tiny/' + sImageSrc;
31949                }
31950            }
31951
31952            document.location.replace('recentproduct://setinfo?simg_src=' + sImageSrc);
31953        }
31954    }
31955};
31956
31957var EC_MANAGE_PRODUCT_RECENT = CAFE24.getDeprecatedNamespace('EC_MANAGE_PRODUCT_RECENT');
31958
31959CAFE24.MANAGE_MEMBER = {
31960    // 카카오싱크 로그인
31961    kakaosyncLogin: function(clientSecret) {
31962        if (Kakao.isInitialized()) {
31963            Kakao.cleanup();
31964        }
31965        Kakao.init(clientSecret);
31966
31967        Kakao.Auth.authorize({
31968            redirectUri: location.origin + EC_ROUTE.getPrefixUrl('/Api/Member/Oauth2ClientCallback/kakao/'),
31969            throughTalk: true
31970        });
31971    }
31972};
31973
31974var EC_MANAGE_MEMBER = CAFE24.getDeprecatedNamespace('EC_MANAGE_MEMBER');
31975
31976// 프로토콜 체크 포함
31977if (window.navigator && 'serviceWorker' in window.navigator) {
31978    window.addEventListener('load', function () {
31979        var getFrontendConfig = function (sOption) {
31980            if (typeof window.CAFE24 === 'object' && typeof window.CAFE24.FRONTEND === 'object') {
31981                return window.CAFE24.FRONTEND[sOption];
31982            }
31983
31984            return null;
31985        };
31986
31987        var sCafeSWName = '/ind-script/sw.php';
31988        var sA2hsLogKey = 'a2hs_manifest_name';
31989        var oManifest = document.querySelector('link[rel="manifest"]');
31990        var bHasManifest = !!oManifest;
31991        if (bHasManifest === true) {
31992            // start_url 로 들어온 경우 로깅
31993            if (location.pathname === '/' && location.search.indexOf('a2hs=1') !== -1) {
31994                var sManifestName = oManifest.href.split('/').pop();
31995
31996                var sStoredManifestName = localStorage.getItem(sA2hsLogKey);
31997                if (!sStoredManifestName || sStoredManifestName !== sManifestName) {
31998                    if (window.fetch) {
31999                        fetch('/exec/front/manage/a2hs', {
32000                            headers: {
32001                                'Content-Type': 'application/json'
32002                            },
32003                            method: 'POST',
32004                            body: JSON.stringify({
32005                                ua: navigator.userAgent
32006                            })
32007                        }).then(function (oResponse) {
32008                            localStorage.setItem(sA2hsLogKey, sManifestName);
32009                        });
32010                    }
32011                }
32012            }
32013        }
32014
32015        var sUserAgent = (navigator.userAgent || '').toLowerCase();
32016        var sClientWebView = ((sUserAgent.indexOf('android') !== -1 && sUserAgent.indexOf('wv') !== -1)
32017            || sUserAgent.indexOf('cafe24plus') !== -1)
32018            ? 'T'
32019            : 'F';
32020
32021        // 서비스워커 설치 검증
32022        var bIsWebView = getFrontendConfig('IS_WEB_VIEW') === 'T' || sClientWebView === 'T';
32023        var oRegistrationPromise = navigator.serviceWorker.getRegistration('/');
32024        if (!oRegistrationPromise) {
32025            return;
32026        }
32027
32028        oRegistrationPromise.then(function (oSWRegistration) {
32029            var bInstallable = true;
32030            // 등록된 서비스워커가 있을 경우, Cafe24 서비스워커인지 확인
32031            if (oSWRegistration) {
32032                var oSW = oSWRegistration.installing || oSWRegistration.waiting || oSWRegistration.active;
32033                if (oSW && oSW.scriptURL.indexOf(sCafeSWName) === -1) {
32034                    bInstallable = false;
32035                } else if (bIsWebView === true) {
32036                    // cafe24 서비스워커면서 웹뷰 접근일 경우 서비스워커 삭제 (크롬 75.0.3770.67 버전 대응)
32037                    return oSWRegistration.unregister().then(function () {
32038                        return false;
32039                    }).catch(function (oError) {
32040                        if (window.EC_JET && EC_JET.message) {
32041                            EC_JET.message(oError, 'ServiceWorker');
32042                        }
32043                        console.warn('unregisterError => ', oError, oError.message, oError.name);
32044                        return false;
32045                    });
32046                }
32047            }
32048
32049            if (bIsWebView) {
32050                bInstallable = false;
32051            }
32052
32053            return bInstallable;
32054        })
32055        .then(function (bInstallable) {
32056            if (!bInstallable) {
32057                return;
32058            }
32059
32060            var sRevision = getFrontendConfig('FW_MANIFEST_CACHE_REVISION');
32061            if (sRevision) {
32062                sCafeSWName = sCafeSWName + '?v=' + sRevision;
32063            }
32064
32065            return navigator.serviceWorker.register(sCafeSWName, {
32066                scope: '/',
32067                updateViaCache: 'all'
32068            }).catch(function (oError) {
32069                if (window.EC_JET && EC_JET.message) {
32070                    EC_JET.message(oError, 'ServiceWorker');
32071                }
32072                console.warn('registerError => ', oError, oError.message, oError.name);
32073            });
32074        });
32075    });
32076}
32077
32078CAFE24.EXTERNAL_FRONT_APPSCRIPT = {
32079    insertAppScript: function() {
32080        if (typeof CAFE24.APPSCRIPT_ASSIGN_DATA !== "undefined" && Array.isArray(CAFE24.APPSCRIPT_ASSIGN_DATA)) {
32081            while (CAFE24.APPSCRIPT_ASSIGN_DATA.length > 0) {
32082                var oSrcData = CAFE24.APPSCRIPT_ASSIGN_DATA.pop();
32083                CAFE24.EXTERNAL_FRONT_APPSCRIPT.appendAppScript(oSrcData['src'], oSrcData['integrity']);
32084            }
32085        }
32086        if (typeof CAFE24.APPSCRIPT_SOURCE_DATA !== "undefined" && Array.isArray(CAFE24.APPSCRIPT_SOURCE_DATA)) {
32087            while (CAFE24.APPSCRIPT_SOURCE_DATA.length > 0) {
32088                CAFE24.EXTERNAL_FRONT_APPSCRIPT.appendSourceTypeScript(CAFE24.APPSCRIPT_SOURCE_DATA.pop());
32089            }
32090        }
32091    },
32092    appendAppScript: function(sSrc, sIntegrity) {
32093        var js = document.createElement('script');
32094        js.src = sSrc;
32095        // integrity 필드가 존재하는 경우 스크립트 무결성 체킹을 위해 추가.
32096        if (sIntegrity && sIntegrity !== null) {
32097            js.integrity = sIntegrity;
32098            js.crossOrigin = "anonymous";
32099        }
32100        document.body.appendChild(js);
32101    },
32102    appendSourceTypeScript: function (sSrc) {
32103        var js = document.createElement('script');
32104        js.type = 'text/javascript';
32105        js.text = CAFE24.EXTERNAL_FRONT_APPSCRIPT.base64Decode(sSrc);
32106        document.body.appendChild(js);
32107    },
32108    base64Decode: function (sEncoded) {
32109        return decodeURIComponent(atob(sEncoded).split('').map(function(c) {
32110            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
32111        }).join(''));
32112    }
32113};
32114if (document.readyState === 'complete') {
32115    CAFE24.EXTERNAL_FRONT_APPSCRIPT.insertAppScript();
32116} else if (window.addEventListener) {
32117    window.addEventListener('load', CAFE24.EXTERNAL_FRONT_APPSCRIPT.insertAppScript);
32118} else if (window.attachEvent) {
32119    window.attachEvent('onload', CAFE24.EXTERNAL_FRONT_APPSCRIPT.insertAppScript);
32120}
32121
32122var EC_EXTERNAL_FRONT_APPSCRIPT = CAFE24.getDeprecatedNamespace('EC_EXTERNAL_FRONT_APPSCRIPT');
32123
32124/**
32125 * SDK spec interface
32126 */
32127EC_EXTERNAL_UTIL_APP_SPECINTERFACE = {
32128
32129    oLoginProvider: {
32130        member_id: null,
32131        provider: null,
32132        client_id: null
32133    },
32134
32135    oCustomerProvider: {
32136        member_id: null,
32137        provider: null
32138    },
32139
32140    oMemberInfo: {
32141        member_id: null,
32142        group_no: null,
32143        guest_id: null
32144    },
32145
32146    oCustomerIDInfo: {
32147        member_id: null,
32148        guest_id: null
32149    },
32150
32151    oCustomerInfo: {
32152        member_id: null,
32153        name: null,
32154        nick_name: null,
32155        group_name: null,
32156        group_no: null,
32157        email: null,
32158        news_mail: null,
32159        phone: null,
32160        cellphone: null,
32161        sms: null,
32162        birthday: null,
32163        additional_information: null,
32164        authentication_method: null,
32165        created_date: null
32166    },
32167
32168    oDefaultShippingAddress: {
32169        shipping_alias: null,
32170        zip_code: null,
32171        address1: null,
32172        address2: null
32173    },
32174
32175    // @todo deprecated
32176    oMileageInfo: {
32177        available_mileage: null,
32178        returned_mileage: null,
32179        total_mileage: null,
32180        unavailable_mileage: null,
32181        used_mileage: null
32182    },
32183
32184    // @todo deprecated
32185    oDepositInfo: {
32186        all_deposit: null,
32187        member_total_deposit: null,
32188        refund_wait_deposit: null,
32189        total_deposit: null,
32190        used_deposit: null
32191    },
32192
32193    oPointInfo: {
32194        available_point: null,
32195        returned_point: null,
32196        total_point: null,
32197        unavailable_point: null,
32198        used_point: null
32199    },
32200
32201    oCreditInfo: {
32202        all_credit: null,
32203        member_total_credit: null,
32204        refund_wait_credit: null,
32205        total_credit: null,
32206        used_credit: null
32207    },
32208
32209    oCartList: {
32210        shop_no: null,
32211        product_no: null,
32212        additional_option: null,
32213        attached_file_option: null,
32214        basket_product_no: null,
32215        product_price: null,
32216        quantity: null,
32217        selected_product: null,
32218        variant_code: null,
32219        option_id: null,
32220        is_set_product: null,
32221        set_product_no: null,
32222        delvtype: null
32223    },
32224
32225    oCartInfo: {
32226        basket_price: null
32227    },
32228
32229    oCartItemList: {
32230        basket_product_no: null,
32231        product_no: null,
32232        price: null,
32233        option_price: null,
32234        quantity: null,
32235        discount_price: null,
32236        variant_code: null,
32237        product_weight: null,
32238        display_group: null,
32239        quantity_based_discount: null,
32240        non_quantity_based_discount: null,
32241        product_volume: null,
32242        additional_option_values: null,
32243        product_bundle: null,
32244        product_bundle_no: null,
32245        option_id: null,
32246        product_name: null,
32247        product_image: null,
32248        option_value: null,
32249        shipping_fee_type: null,
32250        categories: null
32251    },
32252
32253    oCount: {
32254        count: 0
32255    },
32256
32257    oShopInfo: {
32258        language_code: null,
32259        currency_code: null,
32260        timezone: null
32261    },
32262
32263    oAddedProductToCart : {
32264        shop_no: null,
32265        category_no: null,
32266        quantity: null,
32267        additional_option_value: null,
32268        variant_code: null,
32269        product_bundle: null,
32270        prefaid_shipping_fee: null,
32271        attached_file_option: null,
32272        created_date: null,
32273        product_price: null,
32274        option_price: null,
32275        product_bundle_price: null,
32276        product_no: null,
32277        option_id: null,
32278        product_bundle_no: null,
32279        shipping_type : null,
32280        subscription: null,
32281        subscription_cycle: null,
32282        subscription_shipments_cycle_count: null,
32283        basket_product_no: null,
32284        additional_option_values: null,
32285        product_name: null,
32286        checked_products : null,
32287        option_text : null
32288    },
32289
32290    oOrderItemList: {
32291        order_id: null,
32292        order_item_code: null,
32293        order_status: null
32294    },
32295
32296    oOrderDetailInfo: {
32297        shop_no: null,
32298        order_id: null,
32299        initial_order_amount: null,
32300        actual_order_amount: null,
32301        payment_method: null,
32302        order_date: null,
32303        first_payment_method: null,
32304        items: null
32305    },
32306
32307    oClaimableItemList: {
32308        order_id: null,
32309        item_no: null,
32310        order_item_code: null,
32311        variant_code: null,
32312        product_no: null,
32313        product_code: null,
32314        custom_product_code: null,
32315        custom_variant_code: null,
32316        option_id: null,
32317        quantity: null,
32318        price: null,
32319        order_status: null,
32320        supplier_code: null,
32321        supplier_name: null
32322    },
32323
32324    oPrecreateOrder : {
32325        products: null,
32326        hmac: null,
32327        response_time: null,
32328        order_id: null,
32329        return_notification_url: null,
32330        is_nointerest: null,
32331        nointerest_quota: null
32332    },
32333
32334    oPrecreateOrderProduct : {
32335        shop_no: null,
32336        category_no: null,
32337        quantity: null,
32338        additional_option_values: null,
32339        variant_code: null,
32340        product_bundle: null,
32341        prefaid_shipping_fee: null,
32342        attached_file_option: null,
32343        created_date: null,
32344        product_price: null,
32345        option_price: null,
32346        product_bundle_price: null,
32347        product_no: null,
32348        option_id: null,
32349        product_bundle_no: null,
32350        shipping_type : null,
32351        subscription: null,
32352        subscription_cycle: null,
32353        subscription_shipments_cycle_count: null,
32354        basket_product_no: null,
32355        product_name: null,
32356        checked_products : null,
32357        option_text : null,
32358        product_bundle_list: null
32359    },
32360
32361    oBatchBasketAddItem : {
32362        product_no: null,
32363        variants_code: null,
32364        quantity: null,
32365        options: null,
32366        additional_option_values: null
32367    },
32368
32369    oBatchBasketAddSetItem : {
32370        product_no: null,
32371        variants_code: null,
32372        quantity: null,
32373        bundle_product_components: null
32374    }
32375};
32376
32377/**
32378 * 비동기식 데이터 - App Common ( 앱 공통정보 )
32379 */
32380CAPP_ASYNC_METHODS.aDatasetList.push('AppCommon');
32381CAPP_ASYNC_METHODS.AppCommon = {
32382
32383    STORAGE_KEY: 'AppCommon_' + CAFE24.SDE_SHOP_NUM,
32384
32385    __sGuestId: null,
32386
32387    isUse: function()
32388    {
32389        if (typeof CAFE24.APPSCRIPT_SDK_DATA !== "undefined" && EC$.inArray('application', CAFE24.APPSCRIPT_SDK_DATA) > -1) {
32390            return true;
32391        }
32392
32393        return false;
32394    },
32395
32396    restoreCache: function()
32397    {
32398        // sessionStorage 지원 여부 확인
32399        if (!window.sessionStorage) {
32400            return false;
32401        }
32402
32403        try {
32404            var aStorageData = JSON.parse(window.sessionStorage.getItem(this.STORAGE_KEY));
32405
32406            // expire 체크
32407            if (aStorageData.exp < Date.now()) {
32408                throw 'cache has expired.';
32409            }
32410
32411            // 데이터 체크
32412            if (typeof aStorageData.data.guest_id === 'undefined') {
32413                throw 'Invalid cache data.';
32414            }
32415
32416            // 데이터 복구
32417            this.__sGuestId = aStorageData.data.guest_id;
32418
32419            return true;
32420
32421        } catch (e) {
32422            // 복구 실패시 캐시 삭제
32423            this.removeCache();
32424            return false;
32425        }
32426    },
32427
32428    removeCache: function()
32429    {
32430        // sessionStorage 지원 여부 확인
32431        if (!window.sessionStorage) {
32432            return;
32433        }
32434        // 캐시 삭제
32435        window.sessionStorage.removeItem(this.STORAGE_KEY);
32436    },
32437
32438    setData: function(oData)
32439    {
32440        // sessionStorage 지원 여부 확인
32441        if (!window.sessionStorage) {
32442            return;
32443        }
32444
32445        this.__sGuestId = oData.guest_id || '';
32446
32447        try {
32448            sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify({
32449                exp: Date.now() + (1000 * 60 * 10),
32450                data: this.getData()
32451            }));
32452        } catch (error) {
32453        }
32454    },
32455
32456    execute: function()
32457    {
32458    },
32459
32460    getData: function()
32461    {
32462        return {
32463            guest_id: this.__sGuestId
32464        };
32465    },
32466
32467    setSpecData: function(oSpec, oData) {
32468        var aData = {};
32469        for (var prop in oSpec) {
32470            if (oData.hasOwnProperty(prop) === true) {
32471                aData[prop] = oData[prop];
32472            } else {
32473                aData[prop] = oSpec[prop];
32474            }
32475        }
32476        return aData;
32477    },
32478
32479    setSpecDataMap: function(oSpec, oData, oMapData) {
32480        var aData = {};
32481        for (var prop in oSpec) {
32482            if (oData.hasOwnProperty(oMapData[prop]) === true) {
32483                aData[prop] = oData[oMapData[prop]];
32484            } else {
32485                aData[prop] = oSpec[prop];
32486            }
32487        }
32488        return aData;
32489    },
32490
32491    // sdk function list
32492    getMemberID: function()
32493    {
32494        return CAPP_ASYNC_METHODS.member.getData().member_id;
32495    },
32496
32497    getEncryptedMemberId: function(sClientId)
32498    {
32499        var sUrl = '/shop' + EC_SDE_SHOP_NUM + '/Api/Member/Encryptmemberid';
32500        var aParams = {
32501            'client_id': sClientId
32502        };
32503
32504        var aReturn = null;
32505        EC$.ajax({
32506            dataType: "json",
32507            type: "POST",
32508            url: sUrl,
32509            data: aParams,
32510            async: false,
32511
32512            success: function(response) {
32513                var sReturnCode = response.return_code;
32514                var aEncryptedId = response.result.member_id;
32515
32516                if (sReturnCode === '0000') {
32517                    aEncryptedId = '';
32518                }
32519
32520                aReturn = CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerIDInfo, {member_id: aEncryptedId});
32521            }
32522        });
32523        return aReturn;
32524    },
32525
32526    getLoginProvider: function()
32527    {
32528        var sCookieName = "login_provider_"+CAFE24.SDE_SHOP_NUM;
32529        var sLoginProvider = CAPP_ASYNC_METHODS._getCookie(sCookieName);
32530        if (sLoginProvider === null) {
32531            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oLoginProvider, EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oLoginProvider);
32532        }
32533
32534        var oLoginProvider = JSON.parse(decodeURIComponent(sLoginProvider));
32535        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oLoginProvider, oLoginProvider);
32536    },
32537
32538    getCustomerProvider: function()
32539    {
32540        if (CAPP_ASYNC_METHODS.IS_LOGIN !== true) {
32541            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerProvider, EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerProvider);
32542        }
32543
32544        if (typeof CAPP_ASYNC_METHODS.CustomerProvider.getData().member_id !== 'undefined') {
32545            if (CAPP_ASYNC_METHODS.CustomerProvider.getData().member_id !== null) {
32546                return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerProvider, CAPP_ASYNC_METHODS.CustomerProvider.getData());
32547            }
32548        }
32549
32550        var sUrl = EC_ROUTE.getPrefixUrl('/Api/Member/Customerprovider');
32551
32552        var oReturn = EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerProvider;
32553        EC$.ajax({
32554            dataType: "json",
32555            type: "GET",
32556            url: sUrl,
32557            async: false,
32558
32559            success: function(response) {
32560                var sReturnCode = response.return_code;
32561
32562                if (sReturnCode === '0000') {
32563                    return oReturn;
32564                }
32565                oReturn = response.result;
32566
32567                return oReturn;
32568            }
32569        });
32570        CAPP_ASYNC_METHODS.CustomerProvider.setData(oReturn);
32571        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerProvider, oReturn);
32572    },
32573
32574    getMemberInfo: function()
32575    {
32576        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
32577            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oMemberInfo, {group_no: CAPP_ASYNC_METHODS.member.getData().group_no, member_id: CAPP_ASYNC_METHODS.member.getData().member_id});
32578        } else {
32579            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oMemberInfo, {guest_id: CAPP_ASYNC_METHODS.AppCommon.getData().guest_id});
32580        }
32581    },
32582
32583    getCustomerIDInfo: function()
32584    {
32585        if (CAPP_ASYNC_METHODS.IS_LOGIN === true) {
32586            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerIDInfo, {member_id: CAPP_ASYNC_METHODS.member.getData().member_id});
32587        } else {
32588            return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerIDInfo, {guest_id: CAPP_ASYNC_METHODS.AppCommon.getData().guest_id});
32589        }
32590    },
32591
32592    getCustomerInfo: function()
32593    {
32594        var oMember = CAPP_ASYNC_METHODS.member.getData();
32595        if (oMember.created_date && typeof oMember.created_date === 'string') {
32596            oMember.created_date = oMember.created_date.replace(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}).+/, '$1');
32597        }
32598        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCustomerInfo, oMember);
32599    },
32600
32601    getDefaultShippingAddress: function()
32602    {
32603        var oData = CAPP_ASYNC_METHODS.DefaultShippingAddress.getData();
32604        if (oData === null) {
32605            return null;
32606        }
32607        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oDefaultShippingAddress, oData);
32608    },
32609
32610    // @todo deprecated
32611    getMileageInfo: function()
32612    {
32613        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oMileageInfo, CAPP_ASYNC_METHODS.Mileage.getData());
32614    },
32615
32616     // @todo deprecated
32617    getDepositInfo: function()
32618    {
32619        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oDepositInfo, CAPP_ASYNC_METHODS.Deposit.getData());
32620    },
32621
32622    getPointInfo: function()
32623    {
32624        var oMapData = {
32625            available_point: 'available_mileage',
32626            returned_point: 'returned_mileage',
32627            total_point: 'total_mileage',
32628            unavailable_point: 'unavailable_mileage',
32629            used_point: 'used_mileage'
32630        };
32631
32632        return this.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPointInfo, CAPP_ASYNC_METHODS.Mileage.getData(), oMap
32632Data);
32633    },
32634
32635    getCreditInfo: function()
32636    {
32637        var oMapData = {
32638            all_credit: 'all_deposit',
32639            member_total_credit: 'member_total_deposit',
32640            refund_wait_credit: 'refund_wait_deposit',
32641            total_credit: 'total_deposit',
32642            used_credit: 'used_deposit'
32643        };
32644
32645        return this.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCreditInfo, CAPP_ASYNC_METHODS.Deposit.getData(), oMapData);
32646    },
32647
32648    getCartList: function()
32649    {
32650        var oCartList = EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCartList;
32651        var aCartList = [];
32652        return new Promise(function (resolve, reject) {
32653            CAPP_ASYNC_METHODS.BasketProduct.getData().then(function(oData) {
32654                for (var iKey in oData) {
32655                    aCartList.push(CAPP_ASYNC_METHODS.AppCommon.setSpecData(oCartList, oData[iKey]));
32656                }
32657                resolve(aCartList);
32658            }).catch(function(data) {
32659                reject(data);
32660            });
32661        });
32662    },
32663
32664    getCartInfo: function()
32665    {
32666        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCartInfo, CAPP_ASYNC_METHODS.Basketprice.getData());
32667    },
32668
32669    getCartItemList: function()
32670    {
32671        var aCartItemList = [];
32672        if (typeof aBasketProductData === "undefined" && typeof aBasketProductOrderData === "undefined") {
32673            return aCartItemList;
32674        }
32675
32676        //aBasketProductOrderData : 주문서
32677        //aBasketProductData : 장바구니
32678        var aData = (typeof aBasketProductOrderData !== "undefined") ? aBasketProductOrderData : aBasketProductData;
32679
32680        var oMapData = {
32681            basket_product_no: 'basket_prd_no',
32682            product_no: 'product_no',
32683            price: 'product_price',
32684            option_price: 'opt_price',
32685            quantity: 'product_qty',
32686            discount_price: 'product_sale_price',
32687            variant_code: 'item_code',
32688            product_weight: 'product_weight',
32689            display_group: 'main_cate_no',
32690            quantity_based_discount: 'add_sale_related_qty',
32691            non_quantity_based_discount: 'add_sale_not_related_qty',
32692            product_volume: 'volume_size_serial',
32693            product_bundle: 'is_set_product',
32694            product_bundle_no: 'set_product_no',
32695            option_id: 'opt_id',
32696            product_name: 'product_name',
32697            product_image: 'product_image', //tiny 작은목록이미지
32698            option_value: 'option_str',
32699            shipping_fee_type : 'shipping_fee_type',
32700            categories: 'categories'
32701        };
32702
32703        var idx = 0;
32704        var iOldBpPrdNo = null;
32705        var iNewBpPrdNo = null;
32706        for (var iKey in aData) {
32707            if (typeof aData[iKey].basket_prd_no === "undefined" || aData[iKey].basket_prd_no === null) {
32708                continue;
32709            }
32710            iNewBpPrdNo = aData[iKey].basket_prd_no;
32711            if (iOldBpPrdNo !== iNewBpPrdNo) {
32712                aCartItemList[idx] = this.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCartItemList, aData[iKey], oMapData);
32713
32714                if (aCartItemList[idx]['product_volume'] !== '' && aCartItemList[idx]['product_volume'] !== null && aCartItemList[idx]['product_volume'] !== undefined) {
32715                    var aProductVolume = aCartItemList[idx]['product_volume'].split('|');
32716                    aCartItemList[idx]['product_volume'] = {
32717                        'product_width': parseFloat(aProductVolume[0]),
32718                        'product_height': parseFloat(aProductVolume[1]),
32719                        'product_length': parseFloat(aProductVolume[2]),
32720                    };
32721                } else {
32722                    aCartItemList[idx]['product_volume'] = {
32723                        'product_width': null,
32724                        'product_height': null,
32725                        'product_length': null,
32726                    };
32727                }
32728
32729                if (aCartItemList[idx]['product_bundle_no'] == '0') { //세트상품번호 기본값 null 로 노출되도록
32730                    aCartItemList[idx]['product_bundle_no'] = null;
32731                }
32732
32733                if (aCartItemList[idx]['product_image'] == '') {
32734                    aCartItemList[idx]['product_image'] = null;
32735                }
32736
32737                if (aCartItemList[idx]['option_value'] !== null && aCartItemList[idx]['option_value'].length < 1) {
32738                    aCartItemList[idx]['option_value'] = null;
32739                }
32740
32741                if (aData[iKey].custom_data != null) {
32742                    aCartItemList[idx]['additional_option_values'] = [];
32743                    aCartItemList[idx]['additional_option_values'].push(aData[iKey].custom_data);
32744                }
32745                idx++;
32746
32747            } else {
32748                aCartItemList[idx - 1]['quantity'] += aData[iKey].product_qty;
32749
32750                if (aData[iKey].custom_data != null) {
32751                    aCartItemList[idx - 1]['additional_option_values'].push(aData[iKey].custom_data);
32752                }
32753            }
32754
32755            iOldBpPrdNo = iNewBpPrdNo;
32756        }
32757
32758        return aCartItemList;
32759    },
32760
32761    getCartCount: function()
32762    {
32763        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCount, CAPP_ASYNC_METHODS.Basketcnt.getData());
32764    },
32765
32766    getCouponCount: function()
32767    {
32768        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCount, CAPP_ASYNC_METHODS.Couponcnt.getData());
32769    },
32770
32771    getWishCount: function()
32772    {
32773        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oCount, CAPP_ASYNC_METHODS.Wishcount.getData());
32774    },
32775
32776    getShopInfo: function()
32777    {
32778        return this.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oShopInfo, {language_code: SHOP.getLanguage(), currency_code: SHOP.getCurrency(), timezone: SHOP.getTimezone()});
32779    },
32780
32781    getCartMapData : function () {
32782        var oMapData = {
32783            shop_no: 'shop_no',
32784            category_no: 'main_cate_no',
32785            quantity: 'quantity',
32786            additional_option_value: 'option_add',
32787            variant_code: 'item_code',
32788            product_bundle: 'is_set_product',
32789            prefaid_shipping_fee: 'prd_detail_ship_type',
32790            attached_file_option: 'option_attached_file_info_json',
32791            created_date: 'ins_timestamp',
32792            product_price: 'product_price',
32793            option_price: 'opt_price',
32794            product_bundle_price: 'set_discount_price',
32795            product_no: 'product_no',
32796            option_id: 'opt_id',
32797            product_bundle_no: 'set_product_no',
32798            shipping_type : 'delvtype',
32799            subscription: 'is_subscription',
32800            subscription_shipments_cycle : 'subscription_cycle',
32801            subscription_shipments_cycle_count: 'subscription_cycle_count',
32802            basket_product_no: 'basket_prd_no',
32803            additional_option_values: 'custom_data',
32804            product_name: 'product_name',
32805            checked_products : 'is_prd',
32806            option_text: 'opt_str',
32807            product_bundle_list: 'product_bundle_list'
32808        };
32809        return oMapData;
32810    },
32811
32812    getPrecreateOrderMapData : function () {
32813        var oMapData = {
32814            products: 'products',
32815            hmac: 'hmac',
32816            response_time: 'response_time',
32817            order_id: 'order_id',
32818            return_notification_url: 'return_noty_url',
32819            is_nointerest: 'nointerest',
32820            nointerest_quota: 'nointerestQuota'
32821        };
32822
32823        return oMapData;
32824    },
32825
32826    addCurrentProductToCart: function(mall_id, request_time, app_key, member_id, hmac, isOrderPurpose = 'F', request_data_json = null)
32827    {
32828        if (isOrderPurpose !== 'T') {
32829            isOrderPurpose = 'F';
32830        }
32831
32832        return new Promise(function (resolve, reject) {
32833            new Promise(function (res, rej) {
32834                var aInfo = {
32835                    mall_id: mall_id,
32836                    request_time: request_time,
32837                    app_key: app_key,
32838                    request_member_id: member_id,
32839                    hmac: hmac,
32840                    isOrderPurpose: isOrderPurpose,
32841                    request_data_json: request_data_json
32842                };
32843
32844                var proc = (typeof(set_option_data) === 'undefined') ? product_submit : product_set_submit;
32845                if(proc === 'undefined') reject();
32846               
32847                var procResult = proc('app', '/exec/front/order/basket/', null, aInfo, res);
32848                if (procResult && procResult.result === false) reject(procResult);
32849
32850            }).then(function (data) {
32851                var oMapData =  CAPP_ASYNC_METHODS.AppCommon.getCartMapData();
32852                var aCurrentProductToCartResult = [];
32853                for (var index in data) {
32854                    aCurrentProductToCartResult[index] = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oAddedProductToCart, data[index], oMapData);
32855                }
32856                resolve(aCurrentProductToCartResult);
32857            }).catch(function (data) {
32858                reject(data);
32859            })
32860        });
32861    },
32862
32863    precreateOrder: function(mall_id, request_time, app_key, member_id, hmac)
32864    {
32865        var self = this;
32866        return new Promise(function (resolve, reject) {
32867            var aInfo = {
32868                mall_id: mall_id,
32869                request_time: request_time,
32870                app_key: app_key,
32871                member_id: member_id,
32872                hmac: hmac,
32873                shipping_type : (typeof(delvtype) == "undefined") ? sBasketDelvType : delvtype
32874            };
32875            
32876            self.handleBasketOrCart(mall_id, request_time, app_key, member_id, hmac, null, aInfo).then(function () {
32877                EC$.post('/exec/front/order/reserve/', aInfo, function(data) {
32878
32879                    if (data['result'] == false) {
32880                        reject(data);
32881                    }
32882
32883                    var oPrecreateOrderMapData = CAPP_ASYNC_METHODS.AppCommon.getPrecreateOrderMapData();
32884                    var oOrderProductMapData =  CAPP_ASYNC_METHODS.AppCommon.getCartMapData();
32885                    var aPrecreateOrderResult = [];
32886
32887                    var aSpecData = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrder, data, oPrecreateOrderMapData);
32888
32889                    for (var key in aSpecData) {
32890                        aPrecreateOrderResult[key] = aSpecData[key];
32891                    }
32892
32893                    var prodList = data['products'];
32894                    for (var index in prodList) {
32895                        // temp, amazon, paypal 다 걷어내면 없앨코드
32896                        aPrecreateOrderResult[index] = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrderProduct, prodList[index], oOrderProductMapData);
32897                        aPrecreateOrderResult['products'][index] = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrderProduct, prodList[index], oOrderProductMapData);
32898                    }
32899
32900                    resolve(aPrecreateOrderResult);
32901                }, 'json');
32902            }).catch(function (data) {
32903                reject(data);
32904            })
32905        });
32906    },
32907
32908    calculatePayment: function(mall_id, request_time, app_key, member_id, request_data_json, hmac) // 결제예정금액 계산
32909    {
32910        // request_data_json : mobile, member_id, shipping_type, payment_method, country_code, carrier_id, zipcode, address_full, address_state, points_spent_amount, order_coupons, items
32911        var aRequestData = this.parseRequestDataJson(request_data_json);
32912
32913        var self = this;
32914        return new Promise(function (resolve, reject) {
32915            var aInfo = self.createApiRequestInfo(mall_id, request_time, app_key, member_id, request_data_json, hmac, aRequestData);
32916
32917            self.handleBasketOrCart(mall_id, request_time, app_key, member_id, hmac, request_data_json, aInfo).then(function (addedCartItems) {
32918                EC$.post('/exec/front/order/Calculationcreate/', aInfo, function(data) {
32919                    if (data['result'] === false) {
32920                        reject(data);
32921                    }
32922
32923                    data['added_cart_items'] = addedCartItems || []; //장바구니에 추가된 품목 정보 (addCurrentProductToCart result)
32924                    resolve(data);
32925                }, 'json');
32926            }).catch(function (data) {
32927                reject(data);
32928            })
32929        });
32930    },
32931
32932    /**
32933     * ECHOSTING-608244
32934     * 결제예약 요청 실패 응답을 { code, message } 형태로 정규화한다.
32935     * 응답 본문이 JSON 이 아니거나 비어 있어도 항상 객체를 돌려주어 콜백 미호출을 막는다.
32936     *
32937     * @param {object} oXhr jqXHR
32938     * @returns {{code: number, message: string}}
32939     */
32940    parseReserveErrorResponse: function (oXhr)
32941    {
32942        var oFallback = { code: 422, message: 'Failed to create a preliminary order.' };
32943
32944        if (!oXhr) {
32945            return oFallback;
32946        }
32947
32948        if (oXhr.responseJSON && oXhr.responseJSON.message) {
32949            return { code: 422, message: oXhr.responseJSON.message };
32950        }
32951
32952        return oFallback;
32953    },
32954
32955    precreateOrderV2: function(mall_id, request_time, app_key, member_id, request_data_json, hmac) // 결제예약 V2
32956    {
32957        // request_data_json : mobile, member_id, shipping_type, payment_method, country_code, carrier_id, zipcode, address_full, address_state, points_spent_amount, order_coupons, items
32958        var aRequestData = this.parseRequestDataJson(request_data_json);
32959
32960        var self = this;
32961        return new Promise(function (resolve, reject) {
32962            var aInfo = self.createApiRequestInfo(mall_id, request_time, app_key, member_id, request_data_json, hmac, aRequestData);
32963            
32964            self.handleBasketOrCart(mall_id, request_time, app_key, member_id, hmac, request_data_json, aInfo).then(function () {
32965                // ECHOSTING-608244 : 서버가 precreateOrderV2 호출임을 식별하기 위한 내부 마커
32966                aInfo['sdk_version'] = 'v2';
32967
32968                EC$.post('/exec/front/order/reserve/', aInfo, function(data) {
32969
32970                    if (data['result'] == false) {
32971                        reject(data);
32972                        // ECHOSTING-608244 : reject 후에도 아래 매핑이 계속 실행되던 결함 교정
32973                        return;
32974                    }
32975
32976                    var oPrecreateOrderMapData = CAPP_ASYNC_METHODS.AppCommon.getPre
32976createOrderMapData();
32977                    var oOrderProductMapData =  CAPP_ASYNC_METHODS.AppCommon.getCartMapData();
32978                    var aPrecreateOrderResult = [];
32979
32980                    var aSpecData = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrder, data, oPrecreateOrderMapData);
32981
32982                    for (var key in aSpecData) {
32983                        aPrecreateOrderResult[key] = aSpecData[key];
32984                    }
32985
32986                    var prodList = data['products'];
32987                    for (var index in prodList) {
32988                        // temp, amazon, paypal 다 걷어내면 없앨코드
32989                        aPrecreateOrderResult[index] = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrderProduct, prodList[index], oOrderProductMapData);
32990                        aPrecreateOrderResult['products'][index] = CAPP_ASYNC_METHODS.AppCommon.setSpecDataMap(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oPrecreateOrderProduct, prodList[index], oOrderProductMapData);
32991                    }
32992
32993                    resolve(aPrecreateOrderResult);
32994                }, 'json').fail(function (oXhr) {
32995                    // ECHOSTING-608244 : 네트워크 오류·타임아웃·JSON 파싱 실패에도 반드시 콜백이 불리도록 한다.
32996                    reject(CAPP_ASYNC_METHODS.AppCommon.parseReserveErrorResponse(oXhr));
32997                });
32998            }).catch(function (data) {
32999                reject(data);
33000            })
33001        });
33002    },
33003
33004    getOrderItemList: function(start_date, end_date, order_status, page, count, order_id)
33005    {
33006        count = count === null ? iOrderHistoryLimit : count;
33007        return new Promise(function (resolve, reject) {
33008            if (start_date || end_date || order_status || order_id || page || count != iOrderHistoryLimit ) { //기본조회값과 조건이 다른경우 새롭게 갱신
33009                CAPP_ASYNC_METHODS.OrderHistoryItemList.getAsyncData(start_date, end_date, order_status, page, count, order_id)
33010                    .then(function(oData) {
33011                        resolve(oData);
33012                    }).catch(function(data) {
33013                        reject(data);
33014                    });
33015            } else {
33016                var oData = CAPP_ASYNC_METHODS.OrderHistoryItemList.getData();
33017
33018                // 주문조회페이지에서만 로딩시 페이지 리로드후 반영됨
33019                // async.js 호출과 sdk호출이 순차적이지 않아서 세션스토리지에 데이터를 못가져오는 경우가 발생하여 데이터가 없는경우 async한번더 호출 하도록 수정
33020                if (oData) {
33021                    var oOrderItemList = EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oOrderItemList;
33022                    var aOrderHistoryItemList = [];
33023
33024                    for (var iKey in oData) {
33025                        aOrderHistoryItemList.push(CAPP_ASYNC_METHODS.AppCommon.setSpecData(oOrderItemList, oData[iKey]));
33026                    }
33027                    resolve(aOrderHistoryItemList);
33028                } else {
33029                    CAPP_ASYNC_METHODS.OrderHistoryItemList.getAsyncData(start_date, end_date, order_status, page, count, order_id)
33030                        .then(function(oData) {
33031                            resolve(oData);
33032                        }).catch(function(data) {
33033                            reject(data);
33034                    });
33035                }
33036            }
33037        });
33038    },
33039
33040    getOrderDetailInfo: function (shop_no, order_id) {
33041        return new Promise(function (resolve, reject) {
33042            var oData = CAPP_ASYNC_METHODS.OrderDetailInfo.getData();
33043            if (oData[0] != null) {
33044                for (var iKey in oData){
33045                    if (oData[iKey].order_id == order_id){
33046                        var oOrderDetailInfo = EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oOrderDetailInfo;
33047                        var aOrderDetailInfo = [];
33048
33049                        for (var iKey in oData) {
33050                            aOrderDetailInfo.push(CAPP_ASYNC_METHODS.AppCommon.setSpecData(oOrderDetailInfo, oData[iKey]));
33051                        }
33052                        resolve(aOrderDetailInfo);
33053                    } else {
33054                        CAPP_ASYNC_METHODS.OrderDetailInfo.getAsyncData(shop_no, order_id)
33055                            .then(function (oData) {
33056                                resolve(oData);
33057                            }).catch(function () {
33058                            reject(oData);
33059                        });
33060                    }
33061                }
33062            } else {
33063                CAPP_ASYNC_METHODS.OrderDetailInfo.getAsyncData(shop_no, order_id)
33064                    .then(function (oData) {
33065                        resolve(oData);
33066                    }).catch(function () {
33067                    reject(oData);
33068                });
33069            }
33070        });
33071    },
33072
33073    getClaimableItemList: function (order_id, customer_service_type) {
33074        return new Promise(function (resolve, reject) {
33075            var orderPattern = /^[0-9]{8}-[0-9]{7}$/;
33076
33077            if (order_id !== null && orderPattern.test(order_id) === false) {
33078                reject({code: 422, message: 'order_id is not val
33078id'});
33079            }
33080            if (customer_service_type !== null && $.inArray(customer_service_type, ['C','E','R']) === -1) {
33081                reject({code: 422, message: 'customer_service_type is not valid'});
33082            }
33083            var oData = CAPP_ASYNC_METHODS.ClaimableItemList.getData();
33084            if (oData) {
33085                if (oData[0].order_id === order_id) {
33086                    var oClaimableItemList = EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oClaimableItemList;
33087                    var aClaimableItemList = [];
33088
33089                    for (var iKey in oData) {
33090                        aClaimableItemList.push(CAPP_ASYNC_METHODS.AppCommon.setSpecData(oClaimableItemList, oData[iKey]));
33091                    }
33092                    resolve(aClaimableItemList);
33093                } else {
33094                    CAPP_ASYNC_METHODS.ClaimableItemList.getAsyncData(order_id, customer_service_type)
33095                        .then(function (oData) {
33096                            resolve(oData);
33097                        }).catch(function (oData) {
33098                            reject(oData);
33099                    });
33100                }
33101            } else {
33102                CAPP_ASYNC_METHODS.ClaimableItemList.getAsyncData(order_id, customer_service_type)
33103                    .then(function (oData) {
33104                        resolve(oData);
33105                    }).catch(function (oData) {
33106                        reject(oData);
33107                });
33108            }
33109        });
33110    },
33111
33112    emptyCart: function (basket_shipping_type) {
33113        return new Promise(function (resolve, reject) {
33114            if (basket_shipping_type !== null && EC$.inArray(basket_shipping_type, ['A', 'B']) === -1) {
33115                reject({code: 422, message: '"basket_shipping_type" must be provided as one of "A, B".'});
33116                return;
33117            }
33118
33119            CAPP_ASYNC_METHODS.BasketProduct.deleteAllAsyncData(basket_shipping_type).then(function (oData) {
33120                if (oData.result < 0) {
33121                    reject({code: 500, message: 'Empty shopping cart failed.'});
33122                } else {
33123                    resolve('success');
33124                }
33125            }).catch(function () {
33126                reject({code: 500, message: 'Empty shopping cart failed.'});
33127            });
33128        });
33129    },
33130
33131    deleteCartItems: function (basket_shipping_type, product_list) {
33132        return new Promise(function (resolve, reject) {
33133            // 1) basket_shipping_type 유효성
33134            if (EC$.inArray(basket_shipping_type, ['A', 'B']) === -1) {
33135                reject({code: 422, message: '"basket_shipping_type" must be provided as one of "A, B".'});
33136                return;
33137            }
33138
33139            // 2) product_list 유효성
33140            if (!EC$.isArray(product_list) || product_list.length < 1) {
33141                reject({code: 422, message: '"product_list" is not valid.'});
33142                return;
33143            }
33144            for (var idx in product_list) {
33145                if (product_list[idx] === null || product_list[idx] === undefined) {
33146                    reject({code: 422, message: '"product_list" is not valid.'});
33147                    return;
33148                }
33149            }
33150
33151            CAPP_ASYNC_METHODS.BasketProduct.deleteCartItems(basket_shipping_type, product_list).then(function (oData) {
33152                if (oData.result < 0) {
33153                    reject({code: 500, message: 'Delete Cart Items failed.'});
33154                } else {
33155                    resolve(oData);
33156                }
33157            }).catch(function () {
33158                reject({code: 500, message: 'Delete Cart Items failed.'});
33159            });
33160        });
33161    },
33162
33163    addCart: function (basket_type, prepaid_shipping_fee, product_list) {
33164        return new Promise(function (resolve, reject) {
33165            ////////////////////// 입력값 valid 체크 //////////////////////
33166            if (product_list.length > 10) {
33167                reject({code: 422, message: 'The maximum number of "product_list" has been exceeded. (Maximum number of requests: 10)'});
33168                return;
33169            }
33170            // 1) basket_type valid 체크
33171            if (basket_type !== null && EC$.inArray(basket_type, ['A0000', 'A0001']) === -1) {
33172                reject({code: 422, message: '"basket_type" must be provided as one of "A0000 : General, A0001 : Interest-free".'});
33173                return;
33174            }
33175
33176            // 2) prepaid_shipping_fee 체크
33177            if (prepaid_shipping_fee !== null && EC$.inArray(prepaid_shipping_fee, ['P','C']) === -1) {
33178                reject({code: 422, message: '"prepaid_shipping_fee" should be provided as one of "P : Prepaid, C : Collected".'});
33179                return;
33180            }
33181
33182            var errorProductMapping = {}; // 실패데이터 세팅
33183            var aProductListRequest = []; // 실제 장바구니 exec 에 요청할 상품파라미터
33184
33185            // 3) product_list 유효성 체크
33186            for (var i in product_list) {
33187                var sInvalidField = ''; // 유효하지 않은 필드
33188
33189                if (isNaN(product_list[i]['product_no']) || product_list[i]['product_no'] < 1) {
33190                    sInvalidField = 'product_no';
33191                } else if (isNaN(product_list[i]['quantity']) || product_list[i]['quantity'] < 1) {
33192                    sInvalidField = 'quantity';
33193                } else if (/^P.{11}$/.test(product_list[i]['variants_code']) === false) {
33194                    sInvalidField = 'variants_code';
33195                } else if (Array.isArray(product_list[i]['additional_option_values'])) {
33196                    for (var j in product_list[i]['additional_option_values']) {
33197                        if (product_list[i]['additional_option_values'][j]['key'] != 'item_option_add') {
33198                            sInvalidField = 'additional_option_values.key';
33199                            break;
33200                        }
33201
33202                        if (product_list[i]['additional_option_values'][j]['type'] != 'text') {
33203                            sInvalidField = 'additional_option_values.type';
33204                            break;
33205                        }
33206                    }
33207                }
33208
33209                // 유효하지 않은 필드가 있는 경우
33210                if (sInvalidField !== '') {
33211                    if (errorProductMapping[sprintf('%s is not valid', sInvalidField)] === undefined) {
33212                        errorProductMapping[sprintf('%s is not valid', sInvalidField)] = [];
33213                    }
33214
33215                    errorProductMapping[sprintf('%s is not valid', sInvalidField)].push(
33216                        CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddItem, product_list[i])
33217                    );
33218                } else {
33219                    aProductListRequest.push(product_list[i]); // 파라미터 유효성을 통과한 요청데이터만 장바구니 exec 진행
33220                }
33221            }
33222            ////////////////////// 입력값 valid 체크 //////////////////////
33223
33224            // 장바구니 담기 exec 요청데이터
33225            var addCartParam = CAPP_ASYNC_METHODS.AppCommon.getAddCartParam(basket_type, prepaid_shipping_fee, aProductListRequest);
33226
33227            if (addCartParam.batch_add_item.length === 0) { // 파라미터 valid 체크 이후, 장바구니 insert 할 데이터가 없는 경우
33228                var aResponse = {}; // sdk 응답값
33229
33230                // 실패 데이터 (message, more_info) 세팅
33231                for (let error_message of Object.keys(errorProductMapping)) {
33232                    if (aResponse['errors'] === undefined) {
33233                        aResponse['errors'] = [];
33234                    }
33235                    aResponse['errors'].push({code: 422, message: error_message, more_info: errorProductMapping[error_message]});
33236                }
33237
33238                resolve(aResponse);
33239            }
33240
33241            CAPP_ASYNC_METHODS.BasketProduct.setAsyncData(addCartParam).then(function (oData) {
33242                var aResponse = {}; // sdk 응답값
33243
33244                if (oData.result.code === 0) { // 장바구니 담기 성공
33245                    if (oData.aBatchAddResult.success !== undefined) {
33246                        aResponse['cart'] = [];
33247                        oData.aBatchAddResult.success.forEach(function (successData) {
33248                            aResponse['cart'].push(
33249                                CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddItem, successData)
33250                            );
33251                        });
33252                    }
33253
33254                    if (oData.aBatchAddResult.fail !== undefined) {
33255                        // 장바구니 exec 이후 실패한 데이터는 아래에서 failMapping 에 등록
33256                        oData.aBatchAddResult.fail.forEach(function (failData) {
33257                            var errMsg = failData.errMsg ? failData.errMsg : 'Failed to add the product to the cart';
33258                            if (errorProductMapping[errMsg] === undefined) {
33259                                errorProductMapping[errMsg] = [];
33260                            }
33261
33262                            errorProductMapping[errMsg].push(
33263                                CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddItem, failData)
33264                            );
33265                        });
33266                    }
33267
33268                    // 실패 데이터 (message, more_info) 세팅
33269                    for (let error_message of Object.keys(errorProductMapping)) {
33270                        if (aResponse['errors'] === undefined) {
33271                            aResponse['errors'] = [];
33272                        }
33273                        aResponse['errors'].push({code: 422, message: error_message, more_info: errorProductMapping[error_message]});
33274                    }
33275
33276                    resolve(aResponse);
33277                } else { // 장바구니 담기 전체 실패
33278                    const errorMessage = oData.result === -998
33279                        ? 'You can add up to 400 items to the cart.'
33280                        : 'Failed to add the product to the cart';
33281
33282                    reject({code: 422, message: errorMessage});
33283                }
33284            }).catch(function () {
33285                reject({code: 422, message: 'Failed to add the product to the cart'});
33286            });
33287        });
33288    },
33289    addBundleProductsCart: function (basket_type, prepaid_shipping_fee, product_list) {
33290        return new Promise(function (resolve, reject) {
33291            ////////////////////// 입력값 valid 체크 //////////////////////
33292            if (product_list.length > 10) {
33293                reject({code: 422, message: 'The maximum number of "product_list" has been exceeded. (Maximum number of requests: 10)'});
33294                return;
33295            }
33296            // 1) basket_type valid 체크
33297            if (basket_type !== null && EC$.inArray(basket_type, ['A0000', 'A0001']) === -1) {
33298                reject({code: 422, message: '"basket_type" must be provided as one of "A0000 : General, A0001 : Interest-free".'});
33299                return;
33300            }
33301
33302            // 2) prepaid_shipping_fee 체크
33303            if (prepaid_shipping_fee !== null && EC$.inArray(prepaid_shipping_fee, ['P','C']) === -1) {
33304                reject({code: 422, message: '"prepaid_shipping_fee" should be provided as one of "P : Prepaid, C : Collected".'});
33305                return;
33306            }
33307
33308            var errorProductMapping = {}; // 실패데이터 세팅
33309            var aProductListRequest = []; // 실제 장바구니 exec 에 요청할 상품파라미터
33310
33311            // 3) aProductList 유효성 체크
33312            for (var i = 0; i < product_list.length; i++) {
33313                var sInvalidField = ''; // 유효하지 않은 필드
33314
33315                if (isNaN(product_list[i]['product_no']) || product_list[i]['product_no'] < 1) {
33316                    sInvalidField = 'product_no';
33317                } else if (isNaN(product_list[i]['quantity']) || product_list[i]['quantity'] < 1) {
33318                    sInvalidField = 'quantity';
33319                } else if (typeof product_list[i]['bundle_product_components'] == 'undefined') {
33320                    sInvalidField = 'bundle_product_components';
33321                }
33322
33323                if (Array.isArray(product_list[i]['bundle_product_components'])) {
33324                    if (product_list[i]['bundle_product_components'].length < 1) {
33325                        sInvalidField = 'bundle_product_components';
33326                    }
33327
33328                    for (var j = 0; j < product_list[i]['bundle_product_components'].length; j++) {
33329                        if (Array.isArray(product_list[i]['bundle_product_components'][j]['additional_option_values'])) {
33330                            for (var k = 0; k < product_list[i]['bundle_product_components'][j]['additional_option_values'].length; k++) {
33331                                if (product_list[i]['bundle_product_components'][j]['additional_option_values'][k]['key'] != 'item_option_add') {
33332                                    sInvalidField = 'bundle_product_components.additional_option_values.key';
33333                                    break;
33334                                }
33335
33336                                if (product_list[i]['bundle_product_components'][j]['additional_option_values'][k]['type'] != 'text') {
33337                                    sInvalidField = 'bundle_product_components.additional_option_values.type';
33338                                    break;
33339                                }
33340                            }
33341                        }
33342                    }
33343                }
33344
33345                // 유효하지 않은 필드가 있는 경우
33346                if (sInvalidField !== '') {
33347                    if (errorProductMapping[sprintf('%s is not valid', sInvalidField)] === undefined) {
33348                        errorProductMapping[sprintf('%s is not valid', sInvalidField)] = [];
33349                    }
33350
33351                    errorProductMapping[sprintf('%s is not valid', sInvalidField)].push(
33352                        CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddSetItem, product_list[i])
33353                    );
33354                } else {
33355                    aProductListRequest.push(product_list[i]); // 파라미터 유효성을 통과한 요청데이터만 장바구니 exec 진행
33356                }
33357            }
33358            ////////////////////// 입력값 valid 체크 //////////////////////
33359
33360            // 장바구니 담기 exec 요청데이터
33361            var addCartParam = CAPP_ASYNC_METHODS.AppCommon.getAddSetProductCartParam(basket_type, prepaid_shipping_fee, aProductListRequest);
33362
33363            if (addCartParam.batch_add_item.length === 0) { // 파라미터 valid 체크 이후, 장바구니 insert 할 데이터가 없는 경우
33364                var aResponse = {}; // sdk 응답값
33365
33366                // 실패 데이터 (message, more_info) 세팅
33367                for (let error_message of Object.keys(errorProductMapping)) {
33368                    if (aResponse['errors'] === undefined) {
33369                        aResponse['errors'] = [];
33370                    }
33371                    aResponse['errors'].push({code: 422, message: error_message, more_info: errorProductMapping[error_message]});
33372                }
33373
33374                resolve(aResponse);
33375            }
33376
33377            CAPP_ASYNC_METHODS.BasketProduct.setAsyncData(addCartParam).then(function (oData) {
33378                var aResponse = {}; // sdk 응답값
33379
33380                if (oData.result.code === 0) { // 장바구니 담기 성공
33381                    if (oData.aBatchAddResult.success !== undefined) {
33382                        aResponse['cart'] = [];
33383                        oData.aBatchAddResult.success.forEach(function (successData) {
33384                            aResponse['cart'].push(
33385                                CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddSetItem, successData)
33386                            );
33387                        });
33388                    }
33389
33390                    if (oData.aBatchAddResult.fail !== undefined) {
33391                        // 장바구니 exec 이후 실패한 데이터는 아래에서 failMapping 에 등록
33392                        oData.aBatchAddResult.fail.forEach(function (failData) {
33393                            var errMsg = failData.errMsg ? failData.errMsg : 'Failed to add the product to the cart';
33394                            if (errorProductMapping[errMsg] === undefined) {
33395                                errorProductMapping[errMsg] = [];
33396                            }
33397
33398                            errorProductMapping[errMsg].push(
33399                                CAPP_ASYNC_METHODS.AppCommon.setSpecData(EC_EXTERNAL_UTIL_APP_SPECINTERFACE.oBatchBasketAddSetItem, failData)
33400                            );
33401                        });
33402                    }
33403
33404                    // 실패 데이터 (message, more_info) 세팅
33405                    for (let error_message of Object.keys(errorProductMapping)) {
33406                        if (aResponse['errors'] === undefined) {
33407                            aResponse['errors'] = [];
33408                        }
33409                        aResponse['errors'].push({code: 422, message: error_message, more_info: errorProductMapping[error_message]});
33410                    }
33411
33412                    resolve(aResponse);
33413                } else { // 장바구니 담기 전체 실패
33414                    const errorMessage = oData.result === -998
33415                        ? 'You can add up to 400 items to the cart.'
33416                        : 'Failed to add the product to the cart';
33417
33418                    reject({code: 422, message: errorMessage});
33419                }
33420            }).catch(function () {
33421                reject({code: 422, message: 'Failed to add the product to the cart'});
33422            });
33423        });
33424    },
33425    getAddCartParam: function (basket_type, prepaid_shipping_fee, aProductList) {
33426        var aPostData = {
33427            'command': 'add',
33428            'basket_type': basket_type,
33429            'prd_detail_ship_type': prepaid_shipping_fee,
33430            'is_set_product': 'F',
33431            'batch_add_item': [] // 다수품목 대량 insert 필드
33432        };
33433
33434        for (var idx in aProductList) {
33435            aPostData['batch_add_item'].push({
33436                'product_no': aProductList[idx]['product_no'],
33437                'variants_code': aProductList[idx]['variants_code'],
33438                'quantity': aProductList[idx]['quantity'],
33439                'options': aProductList[idx]['options'], // 상품연동형 옵션정보
33440                'additional_option_values': aProductList[idx]['additional_option_values'] // 사용자 지정 옵션
33441            });
33442        }
33443
33444        return aPostData;
33445    },
33446    getAddSetProductCartParam: function (basket_type, prepaid_shipping_fee, aProductList) {
33447        var aPostData = {
33448            'command': 'add',
33449            'basket_type': basket_type,
33450            'prd_detail_ship_type': prepaid_shipping_fee,
33451            'is_set_product': 'T',
33452            'batch_add_item': [] // 다수품목 대량 insert 필드
33453        };
33454
33455        for (var idx in aProductList) {
33456            aPostData['batch_add_item'].push({
33457                'product_no': aProductList[idx]['product_no'],
33458                'quantity': aProductList[idx]['quantity'],
33459                'bundle_product_components': aProductList[idx]['bundle_product_components'] // 세트 구성 상품
33460            });
33461        }
33462
33463        return aPostData;
33464         },
33465
33466     /**
33467      * 주문 바스켓 페이지 여부 확인 (공통 함수)
33468      * @returns {boolean} ORDER_BASKET 페이지 여부
33469      */
33470     isOrderBasketPage: function() {
33471         const metaTag = document.querySelector('meta[name="path_role"]');
33472         return metaTag && metaTag.content === 'ORDER_BASKET';
33473     },
33474
33475     /**
33476      * JSON 파싱 처리 (공통 함수)
33477      * @param {string} request_data_json JSON 문자열
33478      * @returns {Array} 파싱된 배열 또는 빈 배열
33479      */
33480     parseRequestDataJson: function(request_data_json) {
33481         var aRequestData = [];
33482         if (request_data_json) { // 값이 있고
33483             try {
33484                 aRequestData = JSON.parse(request_data_json); // 파싱 시도
33485             } catch (e) {
33486                 console.error("JSON 파싱 실패:", e);
33487                 aRequestData = []; // 실패 시 기본값
33488             }
33489         } else {
33490             aRequestData = []; // 값이 없을 경우 기본값
33491         }
33492         return aRequestData;
33493     },
33494
33495     /**
33496      * shipping_type 설정 (공통 함수)
33497      * @param {Object} aRequestData 파싱된 요청 데이터
33498      * @returns {string} shipping_type
33499      */
33500     getShippingType: function(aRequestData) {
33501         let shipping_type;
33502         if (aRequestData.shipping_type) {
33503             shipping_type = aRequestData.shipping_type;
33504         } else if (typeof delvtype === "undefined") {
33505             shipping_type = sBasketDelvType;
33506         } else {
33507             shipping_type = delvtype;
33508         }
33509         return shipping_type;
33510     },
33511
33512     /**
33513      * API 요청 정보 객체 생성 (공통 함수)
33514      * @param {string} mall_id 쇼핑몰 ID
33515      * @param {string} request_time 요청 시간
33516      * @param {string} app_key 앱 키
33517      * @param {string} member_id 회원 ID
33518      * @param {string} request_data_json JSON 문자열
33519      * @param {string} hmac HMAC
33520      * @param {Object} aRequestData 파싱된 요청 데이터
33521      * @returns {Object} API 요청 정보 객체
33522      */
33523     createApiRequestInfo: function(mall_id, request_time, app_key, member_id, request_data_json, hmac, aRequestData) {
33524         var shipping_type = this.getShippingType(aRequestData);
33525         
33526         return {
33527             mall_id: mall_id,
33528             request_time: request_time,
33529             app_key: app_key,
33530             mobile: aRequestData.mobile,
33531             member_id: member_id,
33532             shipping_type: shipping_type,
33533             payment_method: aRequestData.payment_method,
33534             country_code: aRequestData.country_code,
33535             carrier_id: aRequestData.carrier_id,
33536             zipcode: aRequestData.zipcode,
33537             address_full: aRequestData.address_full,
33538             address_state: aRequestData.address_state,
33539             points_spent_amount: aRequestData.points_spent_amount,
33540             order_coupons: aRequestData.order_coupons,
33541             items: aRequestData.items,
33542             gifts: aRequestData.gifts, // 선택 사은품 (ECHOSTING-630597)
33543             basket_type: "A0000",
33544             request_data_json: request_data_json,
33545             hmac: hmac,
33546             from: "precreateOrder"
33547         };
33548     },
33549
33550     /**
33551      * 바스켓 처리 또는 장바구니 추가 (공통 함수)
33552      * @param {string} mall_id 쇼핑몰 ID
33553      * @param {string} request_time 요청 시간
33554      * @param {string} app_key 앱 키
33555      * @param {string} member_id 회원 ID
33556      * @param {string} hmac HMAC
33557      * @param {string} request_data_json JSON 문자열
33558      * @param {Object} aInfo API 요청 정보 객체
33559      * @param {boolean} bIsOrderBasket 주문 바스켓 페이지 여부
33560      * @returns {Promise} Promise 객체
33561      */
33562     handleBasketOrCart: function(mall_id, request_time, app_key, member_id, hmac, request_data_json, aInfo) {
33563         let bIsOrderBasket = this.isOrderBasketPage();
33564
33565         return new Promise(function (res, rej) {
33566             if (Basket.orderSelectBasket != undefined && bIsOrderBasket) {
33567                 var result = Basket.orderSelectBasket(this, aInfo['shipping_type'], 'app', res);
33568                 if(result == false) rej();
33569             } else {
33570                 if (request_data_json) {
33571                     // request_data_json이 있는 경우 (calculatePayment, precreateOrderV2)
33572                     CAPP_ASYNC_METHODS.AppCommon.addCurrentProductToCart(mall_id, request_time, app_key, member_id, hmac, 'T', request_data_json).then(function(addedCartItems){
33573                         res(addedCartItems);
33574                     }).catch(function (result) {
33575                         rej(result);
33576                     });
33577                 } else {
33578                     // request_data_json이 없는 경우 (precreateOrder)
33579                     CAPP_ASYNC_METHODS.AppCommon.addCurrentProductToCart(mall_id, request_time, app_key, member_id, hmac, 'T').then(function(addedCartItems){
33580                         res(addedCartItems);
33581                     }).catch(function (result) {
33582                         rej(result);
33583                     });
33584                 }
33585             }
33586         });
33587     }
33588};
33589
33590/**
33591 * 도메인 개선으로 인한 이미지 로드 이슈 케이스를 수정
33592 * @type {{bInit: boolean, init: EC_ROUTE_FIX.init}}
33593 */
33594EC$(function () {
33595    var EC_ROUTE_FIX = {
33596        bInit: false,
33597        init: function () {
33598            if (this.bInit || typeof EC_ROUTE === 'undefined') {
33599                return ;
33600            }
33601            this.bInit = true;
33602
33603            this.setEvent();
33604            this.setFix();
33605        },
33606        setEvent: function () {
33607            this.setErrorSrcEvent();
33608            this.setErrorHrefEvent();
33609        },
33610        setFix: function ()
33611        {
33612            // 스킨 미리보기에서 쿠키가 설정되지 않도록 설정 후 재발할 경우 처리되도록 한다.
33613            // this.setFixDomainCookie();
33614        },
33615        /**
33616         * EP 캐시 대응용 ECHOSTING-475508
33617         * 서버사이드 까지 전달되지 못해 url 에 포함된 도메인 정보가 쿠키에 저장되지 못할 경우
33618         * 실제 유입된 url 과 쿠키에 저장된 도메인 정보가 다를 수 있다.
33619         * 따라서 쿠키가 존재한다면 쿠키에 저장된 정보를 url 에 따라 캐싱된 ep 에 assign 된 javascript 변수를 사용하여
33620         * 재 갱신 시켜 준다.
33621         */
33622        setFixDomainCookie: function ()
33623        {
33624            if (EC$.cookie(EC_ROUTE.EC_DOMAIN_PATH_INFO) === null) {
33625                return;
33626            }
33627            if (typeof EC_ROUTE.isNeedRoute() !== 'undefined' && EC_ROUTE.isNeedRoute() === false) {
33628                EC$.cookie(EC_ROUTE.EC_DOMAIN_PATH_INFO, null, {path: '/', domain: '.' + window.location.host});
33629                return;
33630            }
33631
33632            var oOverwriteCookie = {}, bWrite = false;
33633            if (typeof EC_ROUTE.getShopNo() !== 'undefined'
33634                && EC_ROUTE.getShopNo() > 0) {
33635                oOverwriteCookie['shop_no'] = EC_ROUTE.getShopNo();
33636                bWrite = true;
33637            }
33638            if (typeof EC_ROUTE.getMobile() !== 'undefined'
33639            && EC_ROUTE.getMobile() === true) {
33640                oOverwriteCookie['is_mobile'] = EC_ROUTE.getMobile();
33641                bWrite = true;
33642            }
33643            if (typeof EC_ROUTE.getLanguageCode() !== 'undefined'
33644            && EC_ROUTE.getLanguageCode() !== 'ZZ') {
33645                oOverwriteCookie['language_code'] = EC_ROUTE.getLanguageCode();
33646                bWrite = true;
33647            }
33648            if (typeof EC_ROUTE.getSkinCode() !== 'undefined'
33649            && EC_ROUTE.getSkinCode() !== 'default') {
33650                oOverwriteCookie['skin_code'] = EC_ROUTE.getSkinCode();
33651                bWrite = true;
33652            }
33653
33654            if (bWrite) {
33655                EC$.cookie(EC_ROUTE.EC_DOMAIN_PATH_INFO, JSON.stringify(oOverwriteCookie), {path: '/', domain: '.' + window.location.host});
33656            } else {
33657                EC$.cookie(EC_ROUTE.EC_DOMAIN_PATH_INFO, null, {path: '/', domain: '.' + window.location.host});
33658            }
33659        },
33660        setErrorSrcEvent: function ()
33661        {
33662            EC$("img, input[type='image'], script").on("error", function(e){
33663                this.onerror = null;
33664                if (EC$(this).attr('src') != EC_ROUTE.getPrefixUrl(EC$(this).attr('src'))) {
33665                    this.src = EC_ROUTE.getPrefixUrl(EC$(this).attr('src'));
33666                }
33667            });
33668        },
33669        setErrorHrefEvent: function ()
33670        {
33671            EC$("link").on("error", function(e){
33672                this.onerror = null;
33673                if (EC$(this).attr('href') != EC_ROUTE.getPrefixUrl(EC$(this).attr('href'))) {
33674                    this.href = EC_ROUTE.getPrefixUrl(EC$(this).attr('href'));
33675                }
33676            });
33677        }
33678    };
33679
33680    EC_ROUTE_FIX.init();
33681});
33682

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.